1use std::sync::atomic::{AtomicU64, Ordering};
2
3use smallvec::SmallVec;
4
5use super::node::{NodeId, NodeKind, PaneNode, PaneRef, PanelId};
6
7#[derive(Clone, Copy, PartialEq, Eq, Debug)]
12pub enum RootKind {
13 Split,
14 Any,
15}
16
17pub(crate) type NodePath = SmallVec<[usize; 8]>;
19
20static NEXT_NODE_ID: AtomicU64 = AtomicU64::new(0);
29
30fn next_node_id() -> NodeId {
31 NodeId::from_u64(NEXT_NODE_ID.fetch_add(1, Ordering::Relaxed))
32}
33
34#[derive(Clone, PartialEq, Debug)]
46pub struct PaneTree {
47 root: PaneNode,
48 root_kind: RootKind,
49}
50
51impl PaneTree {
52 pub fn new(root_kind: RootKind) -> Self {
53 let root = PaneNode::new(
54 next_node_id(),
55 NodeKind::Split {
56 axis: gpui::Axis::Horizontal,
57 children: Vec::new(),
58 sizes: Vec::new(),
59 },
60 );
61 Self { root, root_kind }
62 }
63
64 pub fn root(&self) -> &PaneNode {
65 &self.root
66 }
67
68 pub(crate) fn root_mut(&mut self) -> &mut PaneNode {
70 &mut self.root
71 }
72
73 pub fn root_kind(&self) -> RootKind {
74 self.root_kind
75 }
76
77 pub(crate) fn allocate_node_id(&mut self) -> NodeId {
78 next_node_id()
79 }
80
81 pub fn node_ids(&self) -> Vec<NodeId> {
83 let mut ids = Vec::new();
84 self.root.walk(&mut |node| ids.push(node.id()));
85 ids
86 }
87
88 pub fn panels(&self) -> impl Iterator<Item = PanelId> {
90 let mut found = Vec::new();
91 self.root.walk(&mut |node| match node.kind() {
92 PaneRef::Tabs { panels, .. } => found.extend_from_slice(panels),
93 PaneRef::Split { .. } => {}
94 });
95 found.into_iter()
96 }
97
98 pub fn find_node(&self, id: NodeId) -> Option<&PaneNode> {
99 self.path_of_node(id).map(|path| self.node_at(&path))
100 }
101
102 pub fn find_panel_node(&self, panel: PanelId) -> Option<NodeId> {
104 let mut found = None;
105 self.root.walk(&mut |node| {
106 let holds = match node.kind() {
107 PaneRef::Tabs { panels, .. } => panels.contains(&panel),
108 PaneRef::Split { .. } => false,
109 };
110 if holds {
111 found = Some(node.id());
112 }
113 });
114 found
115 }
116
117 pub(crate) fn path_of_node(&self, id: NodeId) -> Option<NodePath> {
118 fn search(node: &PaneNode, id: NodeId, path: &mut NodePath) -> bool {
119 if node.id() == id {
120 return true;
121 }
122 if let NodeKind::Split { children, .. } = node.kind_ref() {
123 for (ix, child) in children.iter().enumerate() {
124 path.push(ix);
125 if search(child, id, path) {
126 return true;
127 }
128 path.pop();
129 }
130 }
131 false
132 }
133
134 let mut path = NodePath::new();
135 search(&self.root, id, &mut path).then_some(path)
136 }
137
138 pub(crate) fn node_at(&self, path: &NodePath) -> &PaneNode {
139 let mut node = &self.root;
140 for ix in path {
141 let NodeKind::Split { children, .. } = node.kind_ref() else {
142 unreachable!("path traverses a non-split node");
143 };
144 node = &children[*ix];
145 }
146 node
147 }
148
149 pub(crate) fn node_at_mut(&mut self, path: &NodePath) -> &mut PaneNode {
150 let mut node = &mut self.root;
151 for ix in path {
152 let NodeKind::Split { children, .. } = node.kind_mut() else {
153 unreachable!("path traverses a non-split node");
154 };
155 node = &mut children[*ix];
156 }
157 node
158 }
159
160 pub(crate) fn replace_root(&mut self, node: PaneNode) {
163 self.root = node;
164 }
165}
166
167#[cfg(test)]
168use gpui::{Axis, Pixels};
169
170#[cfg(test)]
171impl PaneTree {
172 pub(crate) fn push_tabs_for_test(&mut self, parent: NodeId, panels: Vec<PanelId>) -> NodeId {
173 let id = self.allocate_node_id();
174 let path = self.path_of_node(parent).expect("parent must exist");
175 let NodeKind::Split {
176 children, sizes, ..
177 } = self.node_at_mut(&path).kind_mut()
178 else {
179 panic!("parent must be a split");
180 };
181 children.push(PaneNode::new(
182 id,
183 NodeKind::Tabs {
184 panels,
185 active_ix: 0,
186 },
187 ));
188 sizes.push(None);
189 id
190 }
191
192 pub(crate) fn push_sized_tabs_for_test(
197 &mut self,
198 parent: NodeId,
199 panels: Vec<PanelId>,
200 size: Option<Pixels>,
201 ) -> NodeId {
202 let id = self.allocate_node_id();
203 let path = self.path_of_node(parent).expect("parent must exist");
204 let NodeKind::Split {
205 children, sizes, ..
206 } = self.node_at_mut(&path).kind_mut()
207 else {
208 panic!("parent must be a split");
209 };
210 children.push(PaneNode::new(
211 id,
212 NodeKind::Tabs {
213 panels,
214 active_ix: 0,
215 },
216 ));
217 sizes.push(size);
218 id
219 }
220
221 pub(crate) fn set_root_split_for_test(&mut self, axis: Axis) -> NodeId {
222 let id = self.allocate_node_id();
223 self.root = PaneNode::new(
224 id,
225 NodeKind::Split {
226 axis,
227 children: Vec::new(),
228 sizes: Vec::new(),
229 },
230 );
231 id
232 }
233
234 pub(crate) fn set_root_axis_for_test(&mut self, new_axis: Axis) {
235 if let NodeKind::Split { axis, .. } = self.root.kind_mut() {
236 *axis = new_axis;
237 }
238 }
239
240 pub(crate) fn set_root_tabs_for_test(
241 &mut self,
242 panels: Vec<PanelId>,
243 active_ix: usize,
244 ) -> NodeId {
245 let id = self.allocate_node_id();
246 self.root = PaneNode::new(id, NodeKind::Tabs { panels, active_ix });
247 id
248 }
249
250 pub(crate) fn push_split_for_test(
251 &mut self,
252 parent: NodeId,
253 axis: Axis,
254 size: Option<Pixels>,
255 ) -> NodeId {
256 let id = self.allocate_node_id();
257 let path = self.path_of_node(parent).expect("parent must exist");
258 let NodeKind::Split {
259 children, sizes, ..
260 } = self.node_at_mut(&path).kind_mut()
261 else {
262 panic!("parent must be a split");
263 };
264 children.push(PaneNode::new(
265 id,
266 NodeKind::Split {
267 axis,
268 children: Vec::new(),
269 sizes: Vec::new(),
270 },
271 ));
272 sizes.push(size);
273 id
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 fn panel_id(n: u64) -> PanelId {
282 PanelId::from_u64(n)
283 }
284
285 #[test]
286 fn a_fresh_split_root_tree_has_no_panels() {
287 let tree = PaneTree::new(RootKind::Split);
288 assert!(
289 matches!(tree.root().kind(), PaneRef::Split { children, .. } if children.is_empty())
290 );
291 assert_eq!(tree.panels().count(), 0);
292 }
293
294 #[test]
295 fn node_ids_are_unique_and_resolvable() {
296 let mut tree = PaneTree::new(RootKind::Split);
297 let root = tree.root().id();
298 let tabs = tree.push_tabs_for_test(root, vec![panel_id(1)]);
299 assert_ne!(tabs, tree.root().id());
300 assert!(tree.find_node(tabs).is_some());
301 assert_eq!(tree.find_panel_node(panel_id(1)), Some(tabs));
302 }
303}