1use gpui::Axis;
2
3use crate::{items::ItemId, workspace::WorkspaceId};
4
5use super::Db;
6
7// We have an many-branched, unbalanced tree with three types:
8// Pane Groups
9// Panes
10// Items
11
12// The root is always a Pane Group
13// Pane Groups can have 0 (or more) Panes and/or Pane Groups as children
14// Panes can have 0 or more items as children
15// Panes can be their own root
16// Items cannot have children
17// References pointing down is hard (SQL doesn't like arrays)
18// References pointing up is easy (1-1 item / parent relationship) but is harder to query
19//
20
21#[derive(Debug, PartialEq, Eq, Copy, Clone)]
22pub struct PaneId {
23 workspace_id: WorkspaceId,
24 pane_id: usize,
25}
26
27#[derive(Debug, PartialEq, Eq, Copy, Clone)]
28pub struct PaneGroupId {
29 workspace_id: WorkspaceId,
30 group_id: usize,
31}
32
33impl PaneGroupId {
34 pub fn root(workspace_id: WorkspaceId) -> Self {
35 Self {
36 workspace_id,
37 group_id: 0,
38 }
39 }
40}
41
42#[derive(Debug, PartialEq, Eq)]
43pub struct SerializedPaneGroup {
44 group_id: PaneGroupId,
45 axis: Axis,
46 children: Vec<PaneGroupChild>,
47}
48
49impl SerializedPaneGroup {
50 pub fn empty_root(workspace_id: WorkspaceId) -> Self {
51 Self {
52 group_id: PaneGroupId::root(workspace_id),
53 axis: Default::default(),
54 children: Default::default(),
55 }
56 }
57}
58
59struct PaneGroupChildRow {
60 child_pane_id: Option<usize>,
61 child_group_id: Option<usize>,
62 index: usize,
63}
64
65#[derive(Debug, PartialEq, Eq)]
66pub enum PaneGroupChild {
67 Pane(SerializedPane),
68 Group(SerializedPaneGroup),
69}
70
71#[derive(Debug, PartialEq, Eq)]
72pub struct SerializedPane {
73 pane_id: PaneId,
74 children: Vec<ItemId>,
75}
76
77pub(crate) const PANE_M_1: &str = "
78CREATE TABLE dock_panes(
79 dock_pane_id INTEGER PRIMARY KEY,
80 workspace_id INTEGER NOT NULL,
81 anchor_position TEXT NOT NULL, -- Enum: 'Bottom' / 'Right' / 'Expanded'
82 shown INTEGER NOT NULL, -- Boolean
83 FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE
84) STRICT;
85
86CREATE TABLE pane_groups(
87 group_id INTEGER PRIMARY KEY,
88 workspace_id INTEGER NOT NULL,
89 parent_group INTEGER, -- NULL indicates that this is a root node
90 axis TEXT NOT NULL, -- Enum: 'Vertical' / 'Horizontal'
91 FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
92 FOREIGN KEY(parent_group) REFERENCES pane_groups(group_id) ON DELETE CASCADE
93) STRICT;
94
95CREATE TABLE grouped_panes(
96 pane_id INTEGER PRIMARY KEY,
97 workspace_id INTEGER NOT NULL,
98 group_id INTEGER NOT NULL,
99 idx INTEGER NOT NULL,
100 FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
101 FOREIGN KEY(group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE
102) STRICT;
103
104CREATE TABLE items(
105 item_id INTEGER PRIMARY KEY,
106 workspace_id INTEGER NOT NULL,
107 kind TEXT NOT NULL,
108 FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE
109) STRICT;
110
111CREATE TABLE group_items(
112 workspace_id INTEGER NOT NULL,
113 pane_id INTEGER NOT NULL,
114 item_id INTEGER NOT NULL,
115 idx INTEGER NOT NULL,
116 PRIMARY KEY (workspace_id, pane_id, item_id)
117 FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
118 FOREIGN KEY(pane_id) REFERENCES grouped_panes(pane_id) ON DELETE CASCADE,
119 FOREIGN KEY(item_id) REFERENCES items(item_id) ON DELETE CASCADE
120) STRICT;
121
122CREATE TABLE dock_items(
123 workspace_id INTEGER NOT NULL,
124 dock_pane_id INTEGER NOT NULL,
125 item_id INTEGER NOT NULL,
126 idx INTEGER NOT NULL,
127 PRIMARY KEY (workspace_id, dock_pane_id, item_id)
128 FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
129 FOREIGN KEY(dock_pane_id) REFERENCES dock_panes(dock_pane_id) ON DELETE CASCADE,
130 FOREIGN KEY(item_id) REFERENCES items(item_id)ON DELETE CASCADE
131) STRICT;
132";
133
134#[derive(Default, Debug)]
135pub enum DockAnchor {
136 #[default]
137 Bottom,
138 Right,
139 Expanded,
140}
141
142#[derive(Default, Debug)]
143pub struct SerializedDockPane {
144 pub workspace: WorkspaceId,
145 pub anchor_position: DockAnchor,
146 pub shown: bool,
147}
148
149impl Db {
150 pub fn get_pane_group(&self, pane_group_id: PaneGroupId) -> SerializedPaneGroup {
151 let axis = self.get_pane_group_axis(pane_group_id);
152 let mut children: Vec<(usize, PaneGroupChild)> = Vec::new();
153 for child_row in self.get_pane_group_children(pane_group_id) {
154 if let Some(child_pane_id) = child_row.child_pane_id {
155 children.push((
156 child_row.index,
157 PaneGroupChild::Pane(self.get_pane(PaneId {
158 workspace_id: pane_group_id.workspace_id,
159 pane_id: child_pane_id,
160 })),
161 ));
162 } else if let Some(child_group_id) = child_row.child_group_id {
163 children.push((
164 child_row.index,
165 PaneGroupChild::Group(self.get_pane_group(PaneGroupId {
166 workspace_id: pane_group_id.workspace_id,
167 group_id: child_group_id,
168 })),
169 ));
170 }
171 }
172 children.sort_by_key(|(index, _)| *index);
173
174 SerializedPaneGroup {
175 group_id: pane_group_id,
176 axis,
177 children: children.into_iter().map(|(_, child)| child).collect(),
178 }
179 }
180
181 fn get_pane_group_children(
182 &self,
183 _pane_group_id: PaneGroupId,
184 ) -> impl Iterator<Item = PaneGroupChildRow> {
185 Vec::new().into_iter()
186 }
187
188 fn get_pane_group_axis(&self, _pane_group_id: PaneGroupId) -> Axis {
189 unimplemented!();
190 }
191
192 pub fn save_pane_splits(&self, _center_pane_group: SerializedPaneGroup) {
193 // Delete the center pane group for this workspace and any of its children
194 // Generate new pane group IDs as we go through
195 // insert them
196 // Items garbage collect themselves when dropped
197 }
198
199 pub(crate) fn get_pane(&self, _pane_id: PaneId) -> SerializedPane {
200 unimplemented!();
201 }
202
203 pub fn get_dock_pane(&self, _workspace: WorkspaceId) -> Option<SerializedDockPane> {
204 unimplemented!()
205 }
206
207 pub fn save_dock_pane(&self, _dock_pane: SerializedDockPane) {}
208}
209
210#[cfg(test)]
211mod tests {
212
213 use crate::Db;
214
215 use super::{DockAnchor, SerializedDockPane};
216
217 #[test]
218 fn test_basic_dock_pane() {
219 let db = Db::open_in_memory();
220
221 let workspace = db.workspace_for_roots(&["/tmp"]);
222
223 db.save_dock_pane(SerializedDockPane {
224 workspace: workspace.workspace_id,
225 anchor_position: DockAnchor::Expanded,
226 shown: true,
227 });
228
229 let _new_workspace = db.workspace_for_roots(&["/tmp"]);
230 }
231}