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