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