ratatui_toolkit/widgets/code_diff/extensions/file_tree/methods/
add_file.rs

1//! Method for adding a file to an existing tree.
2
3use crate::primitives::tree_view::TreeNode;
4use crate::widgets::code_diff::diff_file_tree::DiffFileEntry;
5use crate::widgets::code_diff::diff_file_tree::DiffFileTree;
6use crate::widgets::code_diff::diff_file_tree::FileStatus;
7
8impl DiffFileTree {
9    /// Adds a file to the tree, creating intermediate directories as needed.
10    ///
11    /// # Arguments
12    ///
13    /// * `path` - The file path (e.g., "src/utils/helper.rs")
14    /// * `status` - The modification status
15    pub fn add_file(&mut self, path: &str, status: FileStatus) {
16        let parts: Vec<&str> = path.split('/').collect();
17
18        if parts.is_empty() {
19            return;
20        }
21
22        if parts.len() == 1 {
23            // Root-level file
24            let entry = DiffFileEntry::file(parts[0], path, status);
25            self.nodes.push(TreeNode::new(entry));
26        } else {
27            // File in a subdirectory
28            let root_dir = parts[0];
29
30            // Find or create the root directory
31            let dir_idx = self
32                .nodes
33                .iter()
34                .position(|n| n.data.is_dir && n.data.name == root_dir);
35
36            if let Some(idx) = dir_idx {
37                // Add to existing directory
38                add_to_directory(&mut self.nodes[idx], &parts[1..], path, status, root_dir);
39            } else {
40                // Create new directory tree
41                let mut dir_node = TreeNode::with_children(
42                    DiffFileEntry::directory(root_dir, root_dir),
43                    Vec::new(),
44                );
45                add_to_directory(&mut dir_node, &parts[1..], path, status, root_dir);
46                self.nodes.push(dir_node);
47            }
48        }
49
50        // Re-sort: directories first, then alphabetically
51        self.nodes.sort_by(|a, b| {
52            let a_is_dir = a.data.is_dir;
53            let b_is_dir = b.data.is_dir;
54            match (a_is_dir, b_is_dir) {
55                (true, false) => std::cmp::Ordering::Less,
56                (false, true) => std::cmp::Ordering::Greater,
57                _ => a.data.name.to_lowercase().cmp(&b.data.name.to_lowercase()),
58            }
59        });
60    }
61}
62
63/// Recursively adds a file to a directory node.
64fn add_to_directory(
65    node: &mut TreeNode<DiffFileEntry>,
66    remaining_parts: &[&str],
67    full_path: &str,
68    status: FileStatus,
69    current_path: &str,
70) {
71    if remaining_parts.is_empty() {
72        return;
73    }
74
75    if remaining_parts.len() == 1 {
76        // This is the file
77        let file_name = remaining_parts[0];
78        let entry = DiffFileEntry::file(file_name, full_path, status);
79        node.children.push(TreeNode::new(entry));
80        node.expandable = true;
81    } else {
82        // This is a subdirectory
83        let subdir_name = remaining_parts[0];
84        let subdir_path = format!("{}/{}", current_path, subdir_name);
85
86        // Find or create the subdirectory
87        let subdir_idx = node
88            .children
89            .iter()
90            .position(|n| n.data.is_dir && n.data.name == subdir_name);
91
92        if let Some(idx) = subdir_idx {
93            add_to_directory(
94                &mut node.children[idx],
95                &remaining_parts[1..],
96                full_path,
97                status,
98                &subdir_path,
99            );
100        } else {
101            let mut subdir_node = TreeNode::with_children(
102                DiffFileEntry::directory(subdir_name, &subdir_path),
103                Vec::new(),
104            );
105            add_to_directory(
106                &mut subdir_node,
107                &remaining_parts[1..],
108                full_path,
109                status,
110                &subdir_path,
111            );
112            node.children.push(subdir_node);
113            node.expandable = true;
114        }
115    }
116
117    // Sort children: directories first, then alphabetically
118    node.children.sort_by(|a, b| {
119        let a_is_dir = a.data.is_dir;
120        let b_is_dir = b.data.is_dir;
121        match (a_is_dir, b_is_dir) {
122            (true, false) => std::cmp::Ordering::Less,
123            (false, true) => std::cmp::Ordering::Greater,
124            _ => a.data.name.to_lowercase().cmp(&b.data.name.to_lowercase()),
125        }
126    });
127}