big_code_analysis/output/
dump.rs1#![allow(clippy::enum_glob_use, clippy::ref_option, clippy::wildcard_imports)]
8
9use std::io::Write;
10
11use termcolor::{Color, ColorChoice, StandardStream, StandardStreamLock};
12
13use crate::node::Node;
14use crate::tools::{color, intense_color};
15
16use crate::traits::*;
17
18pub fn dump_node(
50 code: &[u8],
51 node: &Node,
52 depth: i32,
53 line_start: Option<usize>,
54 line_end: Option<usize>,
55) -> std::io::Result<()> {
56 let stdout = StandardStream::stdout(ColorChoice::Always);
57 let mut stdout = stdout.lock();
58 let ret = dump_tree_helper(
59 code,
60 node,
61 "",
62 true,
63 &mut stdout,
64 depth,
65 &line_start,
66 &line_end,
67 );
68
69 color(&mut stdout, Color::White)?;
70
71 ret
72}
73
74#[allow(clippy::too_many_arguments)]
75fn dump_tree_helper(
76 code: &[u8],
77 node: &Node,
78 prefix: &str,
79 last: bool,
80 stdout: &mut StandardStreamLock,
81 depth: i32,
82 line_start: &Option<usize>,
83 line_end: &Option<usize>,
84) -> std::io::Result<()> {
85 if depth == 0 {
86 return Ok(());
87 }
88
89 let (pref_child, pref) = if node.parent().is_none() {
90 ("", "")
91 } else if last {
92 (" ", "╰─ ")
93 } else {
94 ("│ ", "├─ ")
95 };
96
97 let node_row = node.start_row() + 1;
98 let mut display = true;
99 if let Some(line_start) = line_start {
100 display = node_row >= *line_start;
101 }
102 if let Some(line_end) = line_end {
103 display = display && node_row <= *line_end;
104 }
105
106 if display {
107 color(stdout, Color::Blue)?;
108 write!(stdout, "{prefix}{pref}")?;
109
110 intense_color(stdout, Color::Yellow)?;
111 write!(stdout, "{{{}:{}}} ", node.kind(), node.kind_id())?;
112
113 color(stdout, Color::White)?;
114 write!(stdout, "from ")?;
115
116 color(stdout, Color::Green)?;
117 let (pos_row, pos_column) = node.start_position();
118 write!(stdout, "({}, {}) ", pos_row + 1, pos_column + 1)?;
119
120 color(stdout, Color::White)?;
121 write!(stdout, "to ")?;
122
123 color(stdout, Color::Green)?;
124 let (pos_row, pos_column) = node.end_position();
125 write!(stdout, "({}, {}) ", pos_row + 1, pos_column + 1)?;
126
127 if node.start_row() == node.end_row() {
128 color(stdout, Color::White)?;
129 write!(stdout, ": ")?;
130
131 intense_color(stdout, Color::Red)?;
132 let code = &code[node.start_byte()..node.end_byte()];
133 if let Ok(code) = str::from_utf8(code) {
134 write!(stdout, "{code} ")?;
135 } else {
136 stdout.write_all(code)?;
137 }
138 }
139
140 writeln!(stdout)?;
141 }
142
143 let count = node.child_count();
144 if count != 0 {
145 let prefix = format!("{prefix}{pref_child}");
146 let mut i = count;
147 let mut cursor = node.cursor();
148 cursor.goto_first_child();
149
150 loop {
151 i -= 1;
152 dump_tree_helper(
153 code,
154 &cursor.node(),
155 &prefix,
156 i == 0,
157 stdout,
158 depth - 1,
159 line_start,
160 line_end,
161 )?;
162 if !cursor.goto_next_sibling() {
163 break;
164 }
165 }
166 }
167
168 Ok(())
169}
170
171#[derive(Debug)]
173pub struct DumpCfg {
174 pub line_start: Option<usize>,
179 pub line_end: Option<usize>,
184}
185
186pub struct Dump {
188 _guard: (),
189}
190
191impl Callback for Dump {
192 type Res = std::io::Result<()>;
193 type Cfg = DumpCfg;
194
195 fn call<T: ParserTrait>(cfg: Self::Cfg, parser: &T) -> Self::Res {
196 dump_node(
197 parser.get_code(),
198 &parser.get_root(),
199 -1,
200 cfg.line_start,
201 cfg.line_end,
202 )
203 }
204}
205
206#[cfg(test)]
207#[allow(
208 clippy::float_cmp,
209 clippy::cast_precision_loss,
210 clippy::cast_possible_truncation,
211 clippy::cast_sign_loss,
212 clippy::similar_names,
213 clippy::doc_markdown,
214 clippy::needless_raw_string_hashes,
215 clippy::too_many_lines
216)]
217mod tests {
218 use std::path::PathBuf;
219
220 use crate::{CppParser, ParserTrait};
221
222 use super::*;
223
224 #[test]
225 fn dump_node_non_utf8_source_does_not_panic() {
226 let code = b"char c = '\xff';";
229 let path = PathBuf::from("test.c");
230 let parser = CppParser::new(code.to_vec(), &path, None);
231 let root = parser.get_root();
232 assert!(dump_node(code, &root, -1, None, None).is_ok());
233 }
234}