1use std::collections::HashMap;
2use std::path::PathBuf;
3use std::{fs::read_dir, path::Path};
4
5use eframe::egui::{CollapsingHeader, Ui};
6use egui::{emath, Color32, Pos2, Rect, Stroke, Vec2};
7use lang::{CQuery, JavaQuery, JsQuery, RustQuery, SymbolQuery};
8use lazy_static::lazy_static;
9use tree_sitter::Node;
10use tree_sitter::Parser;
11use uuid::Uuid;
12
13pub mod lang;
14
15#[derive(Clone, PartialEq)]
16pub enum TreeEvent {
17 Clicked(String),
18 None,
19}
20#[derive(Debug, Clone, PartialEq)]
21pub enum TreeType {
22 File,
23 Directory,
24}
25
26#[derive(Clone, Default, Debug)]
27pub struct Tree {
28 pub label: String,
29 full_path: String,
30 select_path: String,
31 children: Vec<Tree>,
32 tree_type: Option<TreeType>,
33 clicked: bool,
34}
35
36impl Tree {
37 pub fn new(name: &str, full_path: &str, tree_type: TreeType) -> Self {
38 Self {
39 label: name.to_owned(),
40 full_path: full_path.to_owned(),
41 children: vec![],
42 tree_type: Some(tree_type),
43 clicked: false,
44 select_path: "".to_owned(),
45 }
46 }
47
48 pub fn ui(&mut self, ui: &mut Ui) -> TreeEvent {
49 let root_name = self.label.clone();
50 self.ui_impl(ui, 0, root_name.as_str())
51 }
52}
53
54impl Tree {
55 fn ui_impl(&mut self, ui: &mut Ui, depth: usize, name: &str) -> TreeEvent {
56 let tree_type = self.tree_type.clone().unwrap_or(TreeType::File);
57 if self.children.len() > 0 || tree_type == TreeType::Directory {
58 return CollapsingHeader::new(name)
59 .default_open(depth < 1)
60 .show(ui, |ui| self.children_ui(ui, depth))
61 .body_returned
62 .unwrap_or(TreeEvent::None);
63 } else {
64 let full_path = self.full_path.clone();
65 if ui
66 .selectable_value(&mut self.select_path, full_path, name)
67 .clicked()
68 {
69 return TreeEvent::Clicked(self.full_path.to_string());
70 }
71 return TreeEvent::None;
72 }
73 }
74
75 pub fn clicked(&self) -> bool {
76 if self.clicked {
77 return true;
78 } else if self.children.len() > 0 {
79 for child in &self.children {
80 if child.clicked() {
81 return true;
82 }
83 }
84 }
85 return false;
86 }
87
88 fn children_ui(&mut self, ui: &mut Ui, depth: usize) -> TreeEvent {
89 for ele in &mut self.children {
90 let name = ele.label.clone();
91 let event = ele.ui_impl(ui, depth + 1, &name);
92 if let TreeEvent::Clicked(_) = event {
93 return event;
94 }
95 }
96 TreeEvent::None
97 }
98}
99pub fn recursion_dir(root_path: &Path, pathes: &mut Vec<PathBuf>, mut root_tree: Tree) -> Tree {
100 if root_path.is_dir() {
101 for entry in read_dir(root_path).expect("Error read Dir") {
102 let dir_entry = entry.expect("Error");
103 let path_buf = dir_entry.path();
104 let is_dir = path_buf.is_dir();
105 let tree_type = if is_dir {
106 TreeType::Directory
107 } else {
108 TreeType::File
109 };
110 let mut tree = Tree::new(
111 path_buf.file_name().unwrap().to_str().unwrap(),
112 path_buf.as_os_str().to_str().unwrap(),
113 tree_type,
114 );
115 if path_buf.is_dir() {
116 tree = recursion_dir(path_buf.as_path(), pathes, tree);
117 } else if path_buf.is_file() {
118 pathes.push(path_buf);
119 }
120 root_tree.children.push(tree);
121 }
122 }
123 return root_tree;
124}
125#[derive(Debug, Clone, Hash, PartialEq, Eq)]
126pub enum CodeBlockType {
127 FUNCTION,
128 METHOD,
129 STRUCT,
130 IMPL,
131 CLASS,
132 CONST,
133 NORMAL,
134 CALL,
135}
136#[derive(Debug, Clone)]
137pub struct CodeNode {
138 id: String,
139 pub label: String,
141 pub block: String,
143 pub file_location: usize,
145 pub file_path: String,
147 level: usize,
149 block_type: CodeBlockType,
151 position: Pos2,
153}
154
155impl Default for CodeNode {
156 fn default() -> Self {
157 Self {
158 block_type: CodeBlockType::NORMAL,
159 id: "".to_owned(),
160 label: "".to_owned(),
161 block: "".to_owned(),
162 file_location: 0,
163 level: 0,
164 file_path: "".to_owned(),
165 position: Pos2::ZERO,
166 }
167 }
168}
169
170impl CodeNode {
171 pub fn new(
172 id: &str,
173 label: &str,
174 block: &str,
175 file_location: usize,
176 block_type: CodeBlockType,
177 level: usize,
178 ) -> Self {
179 Self {
180 id: id.to_owned(),
181 label: label.to_owned(),
182 block: block.to_owned(),
183 file_location: file_location.to_owned(),
184 file_path: "".to_owned(),
185 block_type,
186 position: Pos2::new(0.0, 0.0),
187 level,
188 }
189 }
190}
191#[derive(Clone, Copy)]
192pub struct CodeNodeIndex(usize);
193
194pub struct Edge {
195 from: usize,
196 to: usize,
197}
198
199pub struct Graph {
200 nodes: Vec<CodeNode>,
201 edges: Vec<Edge>,
202 focus_node: Option<CodeNodeIndex>,
203}
204
205lazy_static! {
206 static ref BLOCK_TYPE_DARK_COLORS: HashMap<CodeBlockType, egui::Color32> = {
207 let mut m = HashMap::new();
208 m.insert(CodeBlockType::NORMAL, egui::Color32::DARK_GRAY);
209 m.insert(CodeBlockType::FUNCTION, egui::Color32::DARK_BLUE);
210 m.insert(CodeBlockType::STRUCT, egui::Color32::from_rgb(204, 112, 0));
211 m.insert(CodeBlockType::CONST, egui::Color32::from_rgb(204, 112, 0));
212 m.insert(CodeBlockType::CLASS, egui::Color32::DARK_GREEN);
213 m
214 };
215 static ref BLOCK_TYPE_LIGHT_COLORS: HashMap<CodeBlockType, egui::Color32> = {
216 let mut m = HashMap::new();
217 m.insert(CodeBlockType::NORMAL, egui::Color32::LIGHT_GRAY);
218 m.insert(CodeBlockType::FUNCTION, egui::Color32::LIGHT_BLUE);
219 m.insert(CodeBlockType::STRUCT, egui::Color32::LIGHT_YELLOW);
220 m.insert(CodeBlockType::CONST, egui::Color32::LIGHT_YELLOW);
221 m.insert(CodeBlockType::CLASS, egui::Color32::LIGHT_GREEN);
222 m
223 };
224}
225
226impl Graph {
227 pub fn new() -> Self {
228 Self {
229 nodes: vec![],
230 edges: vec![],
231 focus_node: None,
232 }
233 }
234
235 pub fn get_focus_idx(&mut self) -> Option<CodeNodeIndex> {
236 return self.focus_node;
237 }
238
239 pub fn add_node(&mut self, node: CodeNode) -> CodeNodeIndex {
240 let index = self.nodes.len();
241 self.nodes.push(node);
242 return CodeNodeIndex(index);
243 }
244
245 pub fn add_edge(&mut self, from: CodeNodeIndex, to: CodeNodeIndex) {
246 self.edges.push(Edge {
247 from: from.0,
248 to: to.0,
249 })
250 }
251
252 pub fn clear(&mut self) {
253 self.nodes.clear();
254 self.edges.clear();
255 self.focus_node = None;
256 }
257 pub fn layout(&mut self, ui: &mut Ui) {
261 let (_, painter) = ui.allocate_painter(ui.available_size(), egui::Sense::click());
262 let mut sum_height = 0.0;
263 for (index, node) in self.nodes.iter_mut().enumerate() {
265 let text_size = painter
266 .layout_no_wrap(
267 node.label.clone(),
268 egui::FontId::default(),
269 egui::Color32::WHITE,
270 )
271 .size();
272 node.position = Pos2::new(
273 ui.available_size().x / 2.0 + node.level as f32 * 20.0,
274 index as f32 * 16.0 + sum_height + 32.0,
275 );
276 sum_height += text_size.y;
277 }
278 }
279
280 pub fn ui(&mut self, ui: &mut Ui) -> egui::Response {
281 let (response, painter) =
282 ui.allocate_painter(ui.available_size(), egui::Sense::click_and_drag());
283
284 let focus_stroke_color;
285 let stroke_color;
286 let text_color;
287 let grid_color;
288
289 if ui.ctx().style().visuals.dark_mode {
290 stroke_color = egui::Color32::LIGHT_GRAY;
291 text_color = egui::Color32::WHITE;
292 focus_stroke_color = egui::Color32::LIGHT_BLUE;
293 grid_color = Color32::from_gray(50);
294 } else {
295 focus_stroke_color = egui::Color32::BLUE;
296 stroke_color = egui::Color32::DARK_GRAY;
297 text_color = egui::Color32::DARK_GRAY;
298 grid_color = Color32::from_gray(220);
299 }
300
301 let rect = ui.max_rect();
303
304 let cell_size = 10.0; let stroke = Stroke::new(0.5, grid_color); let mut x = rect.left();
310 while x <= rect.right() {
311 let line = [Pos2::new(x, rect.top()), Pos2::new(x, rect.bottom())];
312 painter.line_segment(line, stroke);
313 x += cell_size;
314 }
315
316 let mut y = rect.top();
318 while y <= rect.bottom() {
319 let line = [Pos2::new(rect.left(), y), Pos2::new(rect.right(), y)];
320 painter.line_segment(line, stroke);
321 y += cell_size;
322 }
323
324 let to_screen = emath::RectTransform::from_to(
325 Rect::from_min_size(Pos2::ZERO, response.rect.size()),
326 response.rect,
327 );
328 let mut node_size_list = vec![];
329
330 for (index, node) in self.nodes.iter_mut().enumerate() {
332 let node_pos = to_screen.transform_pos(node.position);
333 let text_size = painter
334 .layout_no_wrap(
335 node.label.clone(),
336 egui::FontId::default(),
337 egui::Color32::WHITE,
338 )
339 .size();
340
341 node_size_list.push(text_size + Vec2::new(16.0, 8.0));
342
343 let rect = egui::Rect::from_min_size(
344 node_pos,
345 egui::vec2(text_size.x + 16.0, text_size.y + 8.0),
346 );
347 let fill_color = if ui.ctx().style().visuals.dark_mode {
348 BLOCK_TYPE_DARK_COLORS
349 .get(&node.block_type)
350 .copied()
351 .unwrap_or(egui::Color32::DARK_GRAY)
352 } else {
353 BLOCK_TYPE_LIGHT_COLORS
354 .get(&node.block_type)
355 .copied()
356 .unwrap_or(egui::Color32::LIGHT_GRAY)
357 };
358 painter.rect(rect, 5.0, fill_color, Stroke::new(1.0, stroke_color));
359
360 painter.text(
361 node_pos + Vec2::new(8.0, 4.0),
362 egui::Align2::LEFT_TOP,
363 &node.label,
364 egui::FontId::default(),
365 text_color,
366 );
367
368 let point_id = response.id.with(&node.id);
369
370 let node_response = ui.interact(rect, point_id, egui::Sense::click_and_drag());
371 if node_response.dragged() {
372 node.position += node_response.drag_delta();
374 }
375 if node_response.clicked() {
376 self.focus_node = Some(CodeNodeIndex(index));
377 }
378 if let Some(f_node) = self.focus_node {
379 if f_node.0 == index {
380 painter.rect(
383 rect,
384 5.0,
385 egui::Color32::TRANSPARENT,
386 Stroke::new(2.5, focus_stroke_color),
387 );
388 }
389 }
390
391 if response.dragged() {
392 node.position += response.drag_delta();
394 }
395 }
396
397 for edge in &self.edges {
399 let from = to_screen.transform_pos(self.nodes[edge.from].position)
400 + Vec2::new(0.0, node_size_list[edge.from].y / 2.0);
401 let to = to_screen.transform_pos(self.nodes[edge.to].position)
402 + Vec2::new(0.0, node_size_list[edge.to].y / 2.0);
403 painter.line_segment(
404 [from, from + Vec2::new(-10.0, 0.0)],
405 (1.0, egui::Color32::GRAY),
406 );
407 painter.line_segment(
408 [from + Vec2::new(-10.0, 0.0), Pos2::new(from.x - 10.0, to.y)],
409 (1.0, egui::Color32::GRAY),
410 );
411 painter.line_segment(
412 [Pos2::new(from.x - 10.0, to.y), to],
413 (1.0, egui::Color32::GRAY),
414 );
415 }
416 response
417 }
418
419 pub fn get_node(&mut self, index: CodeNodeIndex) -> CodeNode {
420 let default_node = CodeNode::default();
421 let node = self.nodes.get(index.0).unwrap_or(&default_node);
422 return node.clone();
423 }
424
425 pub fn node_index(&mut self, node_id: &str) -> CodeNodeIndex {
426 for (index, node) in self.nodes.iter().enumerate() {
427 if node.id == node_id {
428 return CodeNodeIndex(index);
429 }
430 }
431 CodeNodeIndex(0)
432 }
433}
434
435pub fn valid_file_extention(extension: &str) -> bool {
436 return vec!["rs", "c", "h", "java", "js", "jsx"].contains(&extension);
437}
438
439pub fn get_symbol_query(extention: &str) -> Box<dyn SymbolQuery> {
440 match extention {
441 "rs" => Box::new(RustQuery),
442 "java" => Box::new(JavaQuery),
443 "c" | "h" => Box::new(CQuery),
444 "js" | "jsx" => Box::new(JsQuery),
445 _ => Box::new(RustQuery),
446 }
447}
448
449pub fn fetch_calls(path: &str, code: &str, symbol_query: Box<dyn SymbolQuery>) -> Vec<CodeNode> {
450 let mut parser = Parser::new();
451 parser
452 .set_language(&symbol_query.get_lang())
453 .expect("Error load Rust grammer");
454 let tree = parser.parse(code, None).unwrap();
455 let root_node = tree.root_node();
456 recursion_call(root_node, path, code, &symbol_query)
457}
458
459pub fn recursion_call(
460 node: Node,
461 path: &str,
462 code: &str,
463 symbol_query: &Box<dyn SymbolQuery>,
464) -> Vec<CodeNode> {
465 let mut nodes = vec![];
466 let code_node = symbol_query.get_call(code, &node);
467 if let Some(mut node) = code_node {
468 node.file_path = path.to_string();
469 nodes.push(node);
470 }
471
472 for child in node.children(&mut node.walk()) {
473 let sub_nodes = recursion_call(child, path, code, symbol_query);
474 if sub_nodes.len() > 0 {
475 for sub_node in sub_nodes {
476 nodes.push(sub_node);
477 }
478 }
479 }
480 return nodes;
481}
482pub fn fetch_symbols(
486 path: &str,
487 code: &str,
488 symbol_query: Box<dyn SymbolQuery>,
489 graph: &mut Graph,
490) {
491 let mut parser = Parser::new();
492 parser
493 .set_language(&symbol_query.get_lang())
494 .expect("Error load Rust grammer");
495 let tree = parser.parse(code, None).unwrap();
496 let root_node = tree.root_node();
497 let root_code_node = CodeNode::new(
498 format!("{}", Uuid::new_v4()).as_str(),
499 path,
500 code,
501 0,
502 CodeBlockType::NORMAL,
503 0,
504 );
505 graph.add_node(root_code_node);
506 recursion_outline(
507 root_node,
508 CodeNodeIndex(0),
509 path,
510 code,
511 1,
512 &symbol_query,
513 graph,
514 );
515}
516
517pub fn recursion_outline(
518 node: Node,
519 parent_id: CodeNodeIndex,
520 path: &str,
521 code: &str,
522 level: usize,
523 symbol_query: &Box<dyn SymbolQuery>,
524 graph: &mut Graph,
525) {
526 let mut current_id = parent_id;
527 let code_node = symbol_query.get_definition(code, &node);
528 let mut level = level;
529 if let Some(mut node) = code_node {
530 node.file_path = path.to_string();
531 node.level = level;
532 let index = graph.add_node(node);
533 current_id = index;
534 graph.add_edge(parent_id, index);
535 level += 1;
536 }
537
538 for child in node.children(&mut node.walk()) {
539 recursion_outline(child, current_id, path, code, level, symbol_query, graph)
540 }
541}