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, DevicePixels, 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<DevicePixels>>,
73 pub(crate) fullscreen: bool,
74 pub(crate) centered_layout: bool,
75 pub(crate) display: Option<Uuid>,
76 pub(crate) docks: DockStructure,
77}
78
79#[derive(Debug, PartialEq, Clone, Default)]
80pub struct DockStructure {
81 pub(crate) left: DockData,
82 pub(crate) right: DockData,
83 pub(crate) bottom: DockData,
84}
85
86impl Column for DockStructure {
87 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
88 let (left, next_index) = DockData::column(statement, start_index)?;
89 let (right, next_index) = DockData::column(statement, next_index)?;
90 let (bottom, next_index) = DockData::column(statement, next_index)?;
91 Ok((
92 DockStructure {
93 left,
94 right,
95 bottom,
96 },
97 next_index,
98 ))
99 }
100}
101
102impl Bind for DockStructure {
103 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
104 let next_index = statement.bind(&self.left, start_index)?;
105 let next_index = statement.bind(&self.right, next_index)?;
106 statement.bind(&self.bottom, next_index)
107 }
108}
109
110#[derive(Debug, PartialEq, Clone, Default)]
111pub struct DockData {
112 pub(crate) visible: bool,
113 pub(crate) active_panel: Option<String>,
114 pub(crate) zoom: bool,
115}
116
117impl Column for DockData {
118 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
119 let (visible, next_index) = Option::<bool>::column(statement, start_index)?;
120 let (active_panel, next_index) = Option::<String>::column(statement, next_index)?;
121 let (zoom, next_index) = Option::<bool>::column(statement, next_index)?;
122 Ok((
123 DockData {
124 visible: visible.unwrap_or(false),
125 active_panel,
126 zoom: zoom.unwrap_or(false),
127 },
128 next_index,
129 ))
130 }
131}
132
133impl Bind for DockData {
134 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
135 let next_index = statement.bind(&self.visible, start_index)?;
136 let next_index = statement.bind(&self.active_panel, next_index)?;
137 statement.bind(&self.zoom, next_index)
138 }
139}
140
141#[derive(Debug, PartialEq, Clone)]
142pub(crate) enum SerializedPaneGroup {
143 Group {
144 axis: SerializedAxis,
145 flexes: Option<Vec<f32>>,
146 children: Vec<SerializedPaneGroup>,
147 },
148 Pane(SerializedPane),
149}
150
151#[cfg(test)]
152impl Default for SerializedPaneGroup {
153 fn default() -> Self {
154 Self::Pane(SerializedPane {
155 children: vec![SerializedItem::default()],
156 active: false,
157 })
158 }
159}
160
161impl SerializedPaneGroup {
162 #[async_recursion(?Send)]
163 pub(crate) async fn deserialize(
164 self,
165 project: &Model<Project>,
166 workspace_id: WorkspaceId,
167 workspace: WeakView<Workspace>,
168 cx: &mut AsyncWindowContext,
169 ) -> Option<(Member, Option<View<Pane>>, Vec<Option<Box<dyn ItemHandle>>>)> {
170 match self {
171 SerializedPaneGroup::Group {
172 axis,
173 children,
174 flexes,
175 } => {
176 let mut current_active_pane = None;
177 let mut members = Vec::new();
178 let mut items = Vec::new();
179 for child in children {
180 if let Some((new_member, active_pane, new_items)) = child
181 .deserialize(project, workspace_id, workspace.clone(), cx)
182 .await
183 {
184 members.push(new_member);
185 items.extend(new_items);
186 current_active_pane = current_active_pane.or(active_pane);
187 }
188 }
189
190 if members.is_empty() {
191 return None;
192 }
193
194 if members.len() == 1 {
195 return Some((members.remove(0), current_active_pane, items));
196 }
197
198 Some((
199 Member::Axis(PaneAxis::load(axis.0, members, flexes)),
200 current_active_pane,
201 items,
202 ))
203 }
204 SerializedPaneGroup::Pane(serialized_pane) => {
205 let pane = workspace
206 .update(cx, |workspace, cx| workspace.add_pane(cx).downgrade())
207 .log_err()?;
208 let active = serialized_pane.active;
209 let new_items = serialized_pane
210 .deserialize_to(project, &pane, workspace_id, workspace.clone(), cx)
211 .await
212 .log_err()?;
213
214 if pane.update(cx, |pane, _| pane.items_len() != 0).log_err()? {
215 let pane = pane.upgrade()?;
216 Some((Member::Pane(pane.clone()), active.then(|| pane), new_items))
217 } else {
218 let pane = pane.upgrade()?;
219 workspace
220 .update(cx, |workspace, cx| workspace.force_remove_pane(&pane, cx))
221 .log_err()?;
222 None
223 }
224 }
225 }
226 }
227}
228
229#[derive(Debug, PartialEq, Eq, Default, Clone)]
230pub struct SerializedPane {
231 pub(crate) active: bool,
232 pub(crate) children: Vec<SerializedItem>,
233}
234
235impl SerializedPane {
236 pub fn new(children: Vec<SerializedItem>, active: bool) -> Self {
237 SerializedPane { children, active }
238 }
239
240 pub async fn deserialize_to(
241 &self,
242 project: &Model<Project>,
243 pane: &WeakView<Pane>,
244 workspace_id: WorkspaceId,
245 workspace: WeakView<Workspace>,
246 cx: &mut AsyncWindowContext,
247 ) -> Result<Vec<Option<Box<dyn ItemHandle>>>> {
248 let mut item_tasks = Vec::new();
249 let mut active_item_index = None;
250 let mut preview_item_index = None;
251 for (index, item) in self.children.iter().enumerate() {
252 let project = project.clone();
253 item_tasks.push(pane.update(cx, |_, cx| {
254 if let Some(deserializer) = cx.global::<ItemDeserializers>().get(&item.kind) {
255 deserializer(project, workspace.clone(), workspace_id, item.item_id, cx)
256 } else {
257 Task::ready(Err(anyhow::anyhow!(
258 "Deserializer does not exist for item kind: {}",
259 item.kind
260 )))
261 }
262 })?);
263 if item.active {
264 active_item_index = Some(index);
265 }
266 if item.preview {
267 preview_item_index = Some(index);
268 }
269 }
270
271 let mut items = Vec::new();
272 for item_handle in futures::future::join_all(item_tasks).await {
273 let item_handle = item_handle.log_err();
274 items.push(item_handle.clone());
275
276 if let Some(item_handle) = item_handle {
277 pane.update(cx, |pane, cx| {
278 pane.add_item(item_handle.clone(), true, true, None, cx);
279 })?;
280 }
281 }
282
283 if let Some(active_item_index) = active_item_index {
284 pane.update(cx, |pane, cx| {
285 pane.activate_item(active_item_index, false, false, cx);
286 })?;
287 }
288
289 if let Some(preview_item_index) = preview_item_index {
290 pane.update(cx, |pane, cx| {
291 if let Some(item) = pane.item_for_index(preview_item_index) {
292 pane.set_preview_item_id(Some(item.item_id()), cx);
293 }
294 })?;
295 }
296
297 anyhow::Ok(items)
298 }
299}
300
301pub type GroupId = i64;
302pub type PaneId = i64;
303pub type ItemId = u64;
304
305#[derive(Debug, PartialEq, Eq, Clone)]
306pub struct SerializedItem {
307 pub kind: Arc<str>,
308 pub item_id: ItemId,
309 pub active: bool,
310 pub preview: bool,
311}
312
313impl SerializedItem {
314 pub fn new(kind: impl AsRef<str>, item_id: ItemId, active: bool, preview: bool) -> Self {
315 Self {
316 kind: Arc::from(kind.as_ref()),
317 item_id,
318 active,
319 preview,
320 }
321 }
322}
323
324#[cfg(test)]
325impl Default for SerializedItem {
326 fn default() -> Self {
327 SerializedItem {
328 kind: Arc::from("Terminal"),
329 item_id: 100000,
330 active: false,
331 preview: false,
332 }
333 }
334}
335
336impl StaticColumnCount for SerializedItem {
337 fn column_count() -> usize {
338 4
339 }
340}
341impl Bind for &SerializedItem {
342 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
343 let next_index = statement.bind(&self.kind, start_index)?;
344 let next_index = statement.bind(&self.item_id, next_index)?;
345 let next_index = statement.bind(&self.active, next_index)?;
346 statement.bind(&self.preview, next_index)
347 }
348}
349
350impl Column for SerializedItem {
351 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
352 let (kind, next_index) = Arc::<str>::column(statement, start_index)?;
353 let (item_id, next_index) = ItemId::column(statement, next_index)?;
354 let (active, next_index) = bool::column(statement, next_index)?;
355 let (preview, next_index) = bool::column(statement, next_index)?;
356 Ok((
357 SerializedItem {
358 kind,
359 item_id,
360 active,
361 preview,
362 },
363 next_index,
364 ))
365 }
366}