1use super::SerializedAxis;
2use crate::{item::ItemHandle, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId};
3use anyhow::{Context, Result};
4use async_recursion::async_recursion;
5use db::sqlez::{
6 bindable::{Bind, Column, StaticColumnCount},
7 statement::Statement,
8};
9use gpui::{AsyncWindowContext, Bounds, GlobalPixels, Model, Task, View, WeakView};
10use project::Project;
11use std::{
12 path::{Path, PathBuf},
13 sync::Arc,
14};
15use util::ResultExt;
16use uuid::Uuid;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct WorkspaceLocation(Arc<Vec<PathBuf>>);
20
21impl WorkspaceLocation {
22 pub fn paths(&self) -> Arc<Vec<PathBuf>> {
23 self.0.clone()
24 }
25
26 #[cfg(any(test, feature = "test-support"))]
27 pub fn new<P: AsRef<Path>>(paths: Vec<P>) -> Self {
28 Self(Arc::new(
29 paths
30 .into_iter()
31 .map(|p| p.as_ref().to_path_buf())
32 .collect(),
33 ))
34 }
35}
36
37impl<P: AsRef<Path>, T: IntoIterator<Item = P>> From<T> for WorkspaceLocation {
38 fn from(iterator: T) -> Self {
39 let mut roots = iterator
40 .into_iter()
41 .map(|p| p.as_ref().to_path_buf())
42 .collect::<Vec<_>>();
43 roots.sort();
44 Self(Arc::new(roots))
45 }
46}
47
48impl StaticColumnCount for WorkspaceLocation {}
49impl Bind for &WorkspaceLocation {
50 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
51 bincode::serialize(&self.0)
52 .expect("Bincode serialization of paths should not fail")
53 .bind(statement, start_index)
54 }
55}
56
57impl Column for WorkspaceLocation {
58 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
59 let blob = statement.column_blob(start_index)?;
60 Ok((
61 WorkspaceLocation(bincode::deserialize(blob).context("Bincode failed")?),
62 start_index + 1,
63 ))
64 }
65}
66
67#[derive(Debug, PartialEq, Clone)]
68pub(crate) struct SerializedWorkspace {
69 pub(crate) id: WorkspaceId,
70 pub(crate) location: WorkspaceLocation,
71 pub(crate) center_group: SerializedPaneGroup,
72 pub(crate) bounds: Option<Bounds<GlobalPixels>>,
73 pub(crate) fullscreen: bool,
74 pub(crate) display: Option<Uuid>,
75 pub(crate) docks: DockStructure,
76}
77
78#[derive(Debug, PartialEq, Clone, Default)]
79pub struct DockStructure {
80 pub(crate) left: DockData,
81 pub(crate) right: DockData,
82 pub(crate) bottom: DockData,
83}
84
85impl Column for DockStructure {
86 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
87 let (left, next_index) = DockData::column(statement, start_index)?;
88 let (right, next_index) = DockData::column(statement, next_index)?;
89 let (bottom, next_index) = DockData::column(statement, next_index)?;
90 Ok((
91 DockStructure {
92 left,
93 right,
94 bottom,
95 },
96 next_index,
97 ))
98 }
99}
100
101impl Bind for DockStructure {
102 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
103 let next_index = statement.bind(&self.left, start_index)?;
104 let next_index = statement.bind(&self.right, next_index)?;
105 statement.bind(&self.bottom, next_index)
106 }
107}
108
109#[derive(Debug, PartialEq, Clone, Default)]
110pub struct DockData {
111 pub(crate) visible: bool,
112 pub(crate) active_panel: Option<String>,
113 pub(crate) zoom: bool,
114}
115
116impl Column for DockData {
117 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
118 let (visible, next_index) = Option::<bool>::column(statement, start_index)?;
119 let (active_panel, next_index) = Option::<String>::column(statement, next_index)?;
120 let (zoom, next_index) = Option::<bool>::column(statement, next_index)?;
121 Ok((
122 DockData {
123 visible: visible.unwrap_or(false),
124 active_panel,
125 zoom: zoom.unwrap_or(false),
126 },
127 next_index,
128 ))
129 }
130}
131
132impl Bind for DockData {
133 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
134 let next_index = statement.bind(&self.visible, start_index)?;
135 let next_index = statement.bind(&self.active_panel, next_index)?;
136 statement.bind(&self.zoom, next_index)
137 }
138}
139
140#[derive(Debug, PartialEq, Clone)]
141pub(crate) enum SerializedPaneGroup {
142 Group {
143 axis: SerializedAxis,
144 flexes: Option<Vec<f32>>,
145 children: Vec<SerializedPaneGroup>,
146 },
147 Pane(SerializedPane),
148}
149
150#[cfg(test)]
151impl Default for SerializedPaneGroup {
152 fn default() -> Self {
153 Self::Pane(SerializedPane {
154 children: vec![SerializedItem::default()],
155 active: false,
156 })
157 }
158}
159
160impl SerializedPaneGroup {
161 #[async_recursion(?Send)]
162 pub(crate) async fn deserialize(
163 self,
164 project: &Model<Project>,
165 workspace_id: WorkspaceId,
166 workspace: WeakView<Workspace>,
167 cx: &mut AsyncWindowContext,
168 ) -> Option<(Member, Option<View<Pane>>, Vec<Option<Box<dyn ItemHandle>>>)> {
169 match self {
170 SerializedPaneGroup::Group {
171 axis,
172 children,
173 flexes,
174 } => {
175 let mut current_active_pane = None;
176 let mut members = Vec::new();
177 let mut items = Vec::new();
178 for child in children {
179 if let Some((new_member, active_pane, new_items)) = child
180 .deserialize(project, workspace_id, workspace.clone(), cx)
181 .await
182 {
183 members.push(new_member);
184 items.extend(new_items);
185 current_active_pane = current_active_pane.or(active_pane);
186 }
187 }
188
189 if members.is_empty() {
190 return None;
191 }
192
193 if members.len() == 1 {
194 return Some((members.remove(0), current_active_pane, items));
195 }
196
197 Some((
198 Member::Axis(PaneAxis::load(axis.0, members, flexes)),
199 current_active_pane,
200 items,
201 ))
202 }
203 SerializedPaneGroup::Pane(serialized_pane) => {
204 let pane = workspace
205 .update(cx, |workspace, cx| workspace.add_pane(cx).downgrade())
206 .log_err()?;
207 let active = serialized_pane.active;
208 let new_items = serialized_pane
209 .deserialize_to(project, &pane, workspace_id, workspace.clone(), cx)
210 .await
211 .log_err()?;
212
213 if pane.update(cx, |pane, _| pane.items_len() != 0).log_err()? {
214 let pane = pane.upgrade()?;
215 Some((Member::Pane(pane.clone()), active.then(|| pane), new_items))
216 } else {
217 let pane = pane.upgrade()?;
218 workspace
219 .update(cx, |workspace, cx| workspace.force_remove_pane(&pane, cx))
220 .log_err()?;
221 None
222 }
223 }
224 }
225 }
226}
227
228#[derive(Debug, PartialEq, Eq, Default, Clone)]
229pub struct SerializedPane {
230 pub(crate) active: bool,
231 pub(crate) children: Vec<SerializedItem>,
232}
233
234impl SerializedPane {
235 pub fn new(children: Vec<SerializedItem>, active: bool) -> Self {
236 SerializedPane { children, active }
237 }
238
239 pub async fn deserialize_to(
240 &self,
241 project: &Model<Project>,
242 pane: &WeakView<Pane>,
243 workspace_id: WorkspaceId,
244 workspace: WeakView<Workspace>,
245 cx: &mut AsyncWindowContext,
246 ) -> Result<Vec<Option<Box<dyn ItemHandle>>>> {
247 let mut item_tasks = Vec::new();
248 let mut active_item_index = None;
249 for (index, item) in self.children.iter().enumerate() {
250 let project = project.clone();
251 item_tasks.push(pane.update(cx, |_, cx| {
252 if let Some(deserializer) = cx.global::<ItemDeserializers>().get(&item.kind) {
253 deserializer(project, workspace.clone(), workspace_id, item.item_id, cx)
254 } else {
255 Task::ready(Err(anyhow::anyhow!(
256 "Deserializer does not exist for item kind: {}",
257 item.kind
258 )))
259 }
260 })?);
261 if item.active {
262 active_item_index = Some(index);
263 }
264 }
265
266 let mut items = Vec::new();
267 for item_handle in futures::future::join_all(item_tasks).await {
268 let item_handle = item_handle.log_err();
269 items.push(item_handle.clone());
270
271 if let Some(item_handle) = item_handle {
272 pane.update(cx, |pane, cx| {
273 pane.add_item(item_handle.clone(), true, true, None, cx);
274 })?;
275 }
276 }
277
278 if let Some(active_item_index) = active_item_index {
279 pane.update(cx, |pane, cx| {
280 pane.activate_item(active_item_index, false, false, cx);
281 })?;
282 }
283
284 anyhow::Ok(items)
285 }
286}
287
288pub type GroupId = i64;
289pub type PaneId = i64;
290pub type ItemId = u64;
291
292#[derive(Debug, PartialEq, Eq, Clone)]
293pub struct SerializedItem {
294 pub kind: Arc<str>,
295 pub item_id: ItemId,
296 pub active: bool,
297}
298
299impl SerializedItem {
300 pub fn new(kind: impl AsRef<str>, item_id: ItemId, active: bool) -> Self {
301 Self {
302 kind: Arc::from(kind.as_ref()),
303 item_id,
304 active,
305 }
306 }
307}
308
309#[cfg(test)]
310impl Default for SerializedItem {
311 fn default() -> Self {
312 SerializedItem {
313 kind: Arc::from("Terminal"),
314 item_id: 100000,
315 active: false,
316 }
317 }
318}
319
320impl StaticColumnCount for SerializedItem {
321 fn column_count() -> usize {
322 3
323 }
324}
325impl Bind for &SerializedItem {
326 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
327 let next_index = statement.bind(&self.kind, start_index)?;
328 let next_index = statement.bind(&self.item_id, next_index)?;
329 statement.bind(&self.active, next_index)
330 }
331}
332
333impl Column for SerializedItem {
334 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
335 let (kind, next_index) = Arc::<str>::column(statement, start_index)?;
336 let (item_id, next_index) = ItemId::column(statement, next_index)?;
337 let (active, next_index) = bool::column(statement, next_index)?;
338 Ok((
339 SerializedItem {
340 kind,
341 item_id,
342 active,
343 },
344 next_index,
345 ))
346 }
347}