model.rs

  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}