rust_code_analysis_code_split/
comment_rm.rs1use std::io::{self, Write};
2use std::path::PathBuf;
3
4use crate::checker::Checker;
5
6use crate::tools::*;
7use crate::traits::*;
8
9const CR: [u8; 8192] = [b'\n'; 8192];
10
11pub fn rm_comments<T: ParserTrait>(parser: &T) -> Option<Vec<u8>> {
13 let node = parser.get_root();
14 let mut stack = Vec::new();
15 let mut cursor = node.cursor();
16 let mut spans = Vec::new();
17
18 stack.push(node);
19
20 while let Some(node) = stack.pop() {
21 if T::Checker::is_comment(&node) && !T::Checker::is_useful_comment(&node, parser.get_code())
22 {
23 let lines = node.end_row() - node.start_row();
24 spans.push((node.start_byte(), node.end_byte(), lines));
25 } else {
26 cursor.reset(&node);
27 if cursor.goto_first_child() {
28 loop {
29 stack.push(cursor.node());
30 if !cursor.goto_next_sibling() {
31 break;
32 }
33 }
34 }
35 }
36 }
37 if !spans.is_empty() {
38 Some(remove_from_code(parser.get_code(), spans))
39 } else {
40 None
41 }
42}
43
44fn remove_from_code(code: &[u8], mut spans: Vec<(usize, usize, usize)>) -> Vec<u8> {
45 let mut new_code = Vec::with_capacity(code.len());
46 let mut code_start = 0;
47 for (start, end, lines) in spans.drain(..).rev() {
48 new_code.extend(&code[code_start..start]);
49 if lines != 0 {
50 if lines <= CR.len() {
51 new_code.extend(&CR[..lines]);
52 } else {
53 new_code.resize_with(new_code.len() + lines, || b'\n');
54 }
55 }
56 code_start = end;
57 }
58 if code_start < code.len() {
59 new_code.extend(&code[code_start..]);
60 }
61 new_code
62}
63
64#[derive(Debug)]
66pub struct CommentRmCfg {
67 pub in_place: bool,
69 pub path: PathBuf,
71}
72
73pub struct CommentRm {
74 _guard: (),
75}
76
77impl Callback for CommentRm {
78 type Res = std::io::Result<()>;
79 type Cfg = CommentRmCfg;
80
81 fn call<T: ParserTrait>(cfg: Self::Cfg, parser: &T) -> Self::Res {
82 if let Some(new_source) = rm_comments(parser) {
83 if cfg.in_place {
84 write_file(&cfg.path, &new_source)?;
85 } else if let Ok(new_source) = std::str::from_utf8(&new_source) {
86 println!("{new_source}");
87 } else {
88 io::stdout().write_all(&new_source)?;
89 }
90 }
91 Ok(())
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use std::path::PathBuf;
98
99 use crate::{CcommentParser, ParserTrait};
100
101 use super::rm_comments;
102
103 const SOURCE_CODE: &str = "/* Remove this code block */\n\
104 int a = 42; // Remove this comment\n\
105 // Remove this comment\n\
106 int b = 42;\n\
107 /* Remove\n\
108 * this\n\
109 * comment\n\
110 */";
111
112 const SOURCE_CODE_NO_COMMENTS: &str = "\n\
113 int a = 42; \n\
114 \n\
115 int b = 42;\n\
116 \n\
117 \n\
118 \n\
119 \n";
120
121 #[test]
122 fn ccomment_remove_comments() {
123 let path = PathBuf::from("foo.c");
124 let mut trimmed_bytes = SOURCE_CODE.as_bytes().to_vec();
125 trimmed_bytes.push(b'\n');
126 let parser = CcommentParser::new(trimmed_bytes, &path, None);
127
128 let no_comments = rm_comments(&parser).unwrap();
129
130 assert_eq!(no_comments.as_slice(), SOURCE_CODE_NO_COMMENTS.as_bytes());
131 }
132}