markdown_includes/rustdoc_parse/transform/
rust_remove_comments.rs1use crate::rustdoc_parse::transform::utils::rust_code_block_iterator;
7use crate::rustdoc_parse::transform::DocTransform;
8use crate::rustdoc_parse::utils::ItemOrOther;
9use crate::rustdoc_parse::Doc;
10use std::convert::Infallible;
11
12pub struct DocTransformRustRemoveComments;
13
14impl DocTransformRustRemoveComments {
15 #[must_use]
16 pub fn new() -> DocTransformRustRemoveComments {
17 DocTransformRustRemoveComments
18 }
19}
20
21fn is_line_commented(line: &str) -> bool {
22 line.trim_start().starts_with("# ") || line.trim() == "#"
23}
24
25fn process_code_block(new_doc_str: &mut String, code_block: &str) {
26 let mut first = true;
27
28 for (i, line) in code_block.split('\n').enumerate() {
29 if i == 0 && is_line_commented(line) {
33 while !new_doc_str.ends_with('\n') && !new_doc_str.is_empty() {
34 new_doc_str.pop();
35 }
36 }
37
38 if !is_line_commented(line) {
39 if !first {
40 new_doc_str.push('\n');
41 }
42
43 match line.trim_start().starts_with("##") {
46 true => new_doc_str.push_str(&line.replacen('#', "", 1)),
47 false => new_doc_str.push_str(line),
48 }
49
50 first = false;
51 }
52 }
53}
54
55impl DocTransform for DocTransformRustRemoveComments {
56 type E = Infallible;
57
58 fn transform(&self, doc: &Doc) -> Result<Doc, Infallible> {
59 let mut new_doc_str = String::new();
60
61 for item_or_other in rust_code_block_iterator(&doc.content).complete() {
62 match item_or_other {
63 ItemOrOther::Item(code_block) => {
64 process_code_block(&mut new_doc_str, code_block);
65 }
66 ItemOrOther::Other(other) => {
67 new_doc_str.push_str(other);
68 }
69 }
70 }
71
72 Ok(Doc::from_str(new_doc_str))
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use indoc::indoc;
80 use pretty_assertions::assert_eq;
81
82 #[test]
83 fn test_remove_comments_no_code_block() {
84 let doc_str = indoc! { r#"
85 # The crate
86
87 Look a this code:
88
89 That's all! Have a nice day!
90 "#
91 };
92
93 let expected_str = indoc! { r#"
94 # The crate
95
96 Look a this code:
97
98 That's all! Have a nice day!
99 "#
100 };
101
102 let doc = Doc::from_str(doc_str);
103 let expected = Doc::from_str(expected_str);
104
105 let transform = DocTransformRustRemoveComments::new();
106
107 assert_eq!(transform.transform(&doc).unwrap(), expected);
108 }
109
110 #[test]
111 fn test_remove_comments_fenced_code_block() {
112 let doc_str = indoc! { r#"
113 # The crate
114
115 Look a this code:
116
117 ```
118 println!("Hi");
119 println!("There");
120 ```
121
122 A second one:
123
124 ```
125 # A comment.
126 println!("Hi");
127 println!("There");
128 ```
129
130 And so one:
131
132 ```
133 println!("Hi");
134 # A comment.
135 println!("There");
136 ```
137
138 And so forth:
139
140 ```
141 println!("Hi");
142 println!("There");
143 # A comment.
144 ```
145
146 That's all! Have a nice day!
147 "#
148 };
149
150 let expected_str = indoc! { r#"
151 # The crate
152
153 Look a this code:
154
155 ```
156 println!("Hi");
157 println!("There");
158 ```
159
160 A second one:
161
162 ```
163 println!("Hi");
164 println!("There");
165 ```
166
167 And so one:
168
169 ```
170 println!("Hi");
171 println!("There");
172 ```
173
174 And so forth:
175
176 ```
177 println!("Hi");
178 println!("There");
179 ```
180
181 That's all! Have a nice day!
182 "#
183 };
184
185 let doc = Doc::from_str(doc_str);
186 let expected = Doc::from_str(expected_str);
187
188 let transform = DocTransformRustRemoveComments::new();
189
190 assert_eq!(transform.transform(&doc).unwrap(), expected);
191 }
192
193 #[test]
194 fn test_remove_comments_fenced_code_block_starting_with_whitespace() {
195 let doc_str = indoc! { r#"
196 # The crate
197
198 Look a this code:
199
200 ```
201 println!("Hi");
202 println!("There");
203 ```
204
205 A second one:
206
207 ```
208 # A comment.
209 println!("Hi");
210 println!("There");
211 ```
212
213 And so one:
214
215 ```
216 println!("Hi");
217 # A comment.
218 println!("There");
219 ```
220
221 And so forth:
222
223 ```
224 println!("Hi");
225 println!("There");
226 # A comment.
227 ```
228
229 That's all! Have a nice day!
230 "#
231 };
232
233 let expected_str = indoc! { r#"
234 # The crate
235
236 Look a this code:
237
238 ```
239 println!("Hi");
240 println!("There");
241 ```
242
243 A second one:
244
245 ```
246 println!("Hi");
247 println!("There");
248 ```
249
250 And so one:
251
252 ```
253 println!("Hi");
254 println!("There");
255 ```
256
257 And so forth:
258
259 ```
260 println!("Hi");
261 println!("There");
262 ```
263
264 That's all! Have a nice day!
265 "#
266 };
267
268 let doc = Doc::from_str(doc_str);
269 let expected = Doc::from_str(expected_str);
270
271 let transform = DocTransformRustRemoveComments::new();
272
273 assert_eq!(transform.transform(&doc).unwrap(), expected);
274 }
275
276 #[test]
277 fn test_remove_comments_indent_code_block() {
278 let doc_str = indoc! { r#"
279 # The crate
280
281 Look a this code:
282
283 println!("Hi");
284 println!("There");
285
286 A second one:
287
288 # A comment.
289 println!("Hi");
290 println!("There");
291
292 And so one:
293
294 println!("Hi");
295 # A comment.
296 println!("There");
297
298 And so forth:
299
300 println!("Hi");
301 println!("There");
302 # A comment.
303
304 That's all! Have a nice day!
305 "#
306 };
307
308 let expected_str = indoc! { r#"
309 # The crate
310
311 Look a this code:
312
313 println!("Hi");
314 println!("There");
315
316 A second one:
317
318 println!("Hi");
319 println!("There");
320
321 And so one:
322
323 println!("Hi");
324 println!("There");
325
326 And so forth:
327
328 println!("Hi");
329 println!("There");
330
331 That's all! Have a nice day!
332 "#
333 };
334
335 let doc = Doc::from_str(doc_str);
336 let expected = Doc::from_str(expected_str);
337
338 let transform = DocTransformRustRemoveComments::new();
339
340 assert_eq!(transform.transform(&doc).unwrap(), expected);
341 }
342
343 #[test]
344 fn test_remove_comments_indent_code_block_empty_lines() {
345 let doc_str = indoc! { r#"
346 # The crate
347
348 Look a this code:
349
350 println!("Hi");
351
352 println!("There");
353
354 That's all! Have a nice day!
355 "#
356 };
357
358 let expected_str = indoc! { r#"
359 # The crate
360
361 Look a this code:
362
363 println!("Hi");
364
365 println!("There");
366
367 That's all! Have a nice day!
368 "#
369 };
370
371 let doc = Doc::from_str(doc_str);
372 let expected = Doc::from_str(expected_str);
373
374 let transform = DocTransformRustRemoveComments::new();
375
376 assert_eq!(transform.transform(&doc).unwrap(), expected);
377 }
378
379 #[test]
380 fn test_remove_comments_indent_code_beginning_file_with_comment() {
381 let doc_str = indoc! { r#"
382 # Comment
383 println!("Hi");
384 # x
385 println!("There");
386
387 That's all! Have a nice day!
388 "#
389 };
390
391 assert!(doc_str.starts_with(" #"), "Ensure file starts correctly");
392
393 let expected_str = indoc! { r#"
394 println!("Hi");
395 println!("There");
396
397 That's all! Have a nice day!
398 "#
399 };
400
401 let doc = Doc::from_str(doc_str);
402 let expected = Doc::from_str(expected_str);
403
404 let transform = DocTransformRustRemoveComments::new();
405
406 assert_eq!(transform.transform(&doc).unwrap(), expected);
407 }
408
409 #[test]
410 fn test_remove_comments_indent_code_beginning_file_no_comment() {
411 let doc_str = indoc! { r#"
412 println!("Hi");
413 # x
414 println!("There");
415
416 That's all! Have a nice day!
417 "#
418 };
419
420 assert!(
421 doc_str.starts_with(" println!"),
422 "Ensure file starts correctly"
423 );
424
425 let expected_str = indoc! { r#"
426 println!("Hi");
427 println!("There");
428
429 That's all! Have a nice day!
430 "#
431 };
432
433 let doc = Doc::from_str(doc_str);
434 let expected = Doc::from_str(expected_str);
435
436 let transform = DocTransformRustRemoveComments::new();
437
438 assert_eq!(transform.transform(&doc).unwrap(), expected);
439 }
440
441 #[test]
442 fn test_remove_comments_identify_comment() {
443 let doc_str = indoc! { "
444 # The crate
445
446 Look a this code:
447
448 ```
449 # This is a comment
450 #This is not.
451 println!(\"There\");
452 #
453 # ↑ That line is a comment
454 #\t
455 # ↑ And so is that one.
456 ```
457 "
458 };
459
460 let expected_str = indoc! { r#"
461 # The crate
462
463 Look a this code:
464
465 ```
466 #This is not.
467 println!("There");
468 ```
469 "#
470 };
471
472 let doc = Doc::from_str(doc_str);
473 let expected = Doc::from_str(expected_str);
474
475 let transform = DocTransformRustRemoveComments::new();
476
477 assert_eq!(transform.transform(&doc).unwrap(), expected);
478 }
479
480 #[test]
481 fn test_remove_comments_double_hash_escape_comment() {
482 let doc_str = indoc! { r#"
483 ```
484 if true {
485 ## This is not a comment.
486 ##And neither is this.
487 println!("There");
488 }
489 ```
490 "#
491 };
492
493 let expected_str = indoc! { r#"
494 ```
495 if true {
496 # This is not a comment.
497 #And neither is this.
498 println!("There");
499 }
500 ```
501 "#
502 };
503
504 let doc = Doc::from_str(doc_str);
505 let expected = Doc::from_str(expected_str);
506
507 let transform = DocTransformRustRemoveComments::new();
508
509 assert_eq!(transform.transform(&doc).unwrap(), expected);
510 }
511
512 #[test]
513 fn test_remove_comments_for_known_code_block_tags() {
514 let tags = [
515 "should_panic",
516 "no_run",
517 "ignore",
518 "allow_fail",
519 "rust",
520 "test_harness",
521 "compile_fail",
522 "edition2018",
523 "ignore-foo",
524 ];
525
526 for tag in tags {
527 let doc_str = format!(
528 "```{}\n# This is a comment.\nprintln!(\"#There\");\n```\n",
529 tag
530 );
531
532 let expected_str = format!("```{}\nprintln!(\"#There\");\n```\n", tag);
533
534 let doc = Doc::from_str(doc_str);
535 let expected = Doc::from_str(expected_str);
536
537 let transform = DocTransformRustRemoveComments::new();
538
539 assert_eq!(transform.transform(&doc).unwrap(), expected);
540 }
541 }
542
543 #[test]
544 fn test_remove_comments_for_unknown_code_block_tags_no_change() {
545 let tags = ["text", "bash"];
546
547 for tag in tags {
548 let doc_str = format!(
549 "```{}\n# This is a comment.\nprintln!(\"#There\");\n```\n",
550 tag
551 );
552 let doc = Doc::from_str(doc_str);
553
554 let transform = DocTransformRustRemoveComments::new();
555
556 assert_eq!(transform.transform(&doc).unwrap(), doc);
557 }
558 }
559
560 #[test]
561 fn test_remove_comments_nested_fenced_block() {
562 let doc_str = indoc! { r#"
563 ````
564 # Comment 1
565 let s = "
566 ```
567 ";
568 # Comment 2
569 println!("Hi");
570 let s = "
571 ```
572 ";
573 ````
574 "#
575 };
576
577 let expected_str = indoc! { r#"
578 ````
579 let s = "
580 ```
581 ";
582 println!("Hi");
583 let s = "
584 ```
585 ";
586 ````
587 "#
588 };
589
590 let doc = Doc::from_str(doc_str);
591 let expected = Doc::from_str(expected_str);
592
593 let transform = DocTransformRustRemoveComments::new();
594
595 assert_eq!(transform.transform(&doc).unwrap(), expected);
596 }
597}