model.rs

  1use super::{SerializedAxis, SerializedWindowBounds};
  2use crate::{
  3    Member, Pane, PaneAxis, SerializableItemRegistry, Workspace, WorkspaceId, item::ItemHandle,
  4    path_list::PathList,
  5};
  6use anyhow::Result;
  7use async_recursion::async_recursion;
  8use db::sqlez::{
  9    bindable::{Bind, Column, StaticColumnCount},
 10    statement::Statement,
 11};
 12use gpui::{AsyncWindowContext, Entity, WeakEntity};
 13
 14use project::{Project, debugger::breakpoint_store::SourceBreakpoint};
 15use remote::ssh_session::SshProjectId;
 16use serde::{Deserialize, Serialize};
 17use std::{
 18    collections::BTreeMap,
 19    path::{Path, PathBuf},
 20    sync::Arc,
 21};
 22use util::ResultExt;
 23use uuid::Uuid;
 24
 25#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
 26pub struct SerializedSshConnection {
 27    pub id: SshProjectId,
 28    pub host: String,
 29    pub port: Option<u16>,
 30    pub user: Option<String>,
 31}
 32
 33#[derive(Debug, PartialEq, Clone)]
 34pub enum SerializedWorkspaceLocation {
 35    Local,
 36    Ssh(SerializedSshConnection),
 37}
 38
 39impl SerializedWorkspaceLocation {
 40    /// Get sorted paths
 41    pub fn sorted_paths(&self) -> Arc<Vec<PathBuf>> {
 42        unimplemented!()
 43    }
 44}
 45
 46#[derive(Debug, PartialEq, Clone)]
 47pub(crate) struct SerializedWorkspace {
 48    pub(crate) id: WorkspaceId,
 49    pub(crate) location: SerializedWorkspaceLocation,
 50    pub(crate) paths: PathList,
 51    pub(crate) center_group: SerializedPaneGroup,
 52    pub(crate) window_bounds: Option<SerializedWindowBounds>,
 53    pub(crate) centered_layout: bool,
 54    pub(crate) display: Option<Uuid>,
 55    pub(crate) docks: DockStructure,
 56    pub(crate) session_id: Option<String>,
 57    pub(crate) breakpoints: BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
 58    pub(crate) window_id: Option<u64>,
 59}
 60
 61#[derive(Debug, PartialEq, Clone, Default)]
 62pub struct DockStructure {
 63    pub(crate) left: DockData,
 64    pub(crate) right: DockData,
 65    pub(crate) bottom: DockData,
 66}
 67
 68impl Column for DockStructure {
 69    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
 70        let (left, next_index) = DockData::column(statement, start_index)?;
 71        let (right, next_index) = DockData::column(statement, next_index)?;
 72        let (bottom, next_index) = DockData::column(statement, next_index)?;
 73        Ok((
 74            DockStructure {
 75                left,
 76                right,
 77                bottom,
 78            },
 79            next_index,
 80        ))
 81    }
 82}
 83
 84impl Bind for DockStructure {
 85    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
 86        let next_index = statement.bind(&self.left, start_index)?;
 87        let next_index = statement.bind(&self.right, next_index)?;
 88        statement.bind(&self.bottom, next_index)
 89    }
 90}
 91
 92#[derive(Debug, PartialEq, Clone, Default)]
 93pub struct DockData {
 94    pub(crate) visible: bool,
 95    pub(crate) active_panel: Option<String>,
 96    pub(crate) zoom: bool,
 97}
 98
 99impl Column for DockData {
100    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
101        let (visible, next_index) = Option::<bool>::column(statement, start_index)?;
102        let (active_panel, next_index) = Option::<String>::column(statement, next_index)?;
103        let (zoom, next_index) = Option::<bool>::column(statement, next_index)?;
104        Ok((
105            DockData {
106                visible: visible.unwrap_or(false),
107                active_panel,
108                zoom: zoom.unwrap_or(false),
109            },
110            next_index,
111        ))
112    }
113}
114
115impl Bind for DockData {
116    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
117        let next_index = statement.bind(&self.visible, start_index)?;
118        let next_index = statement.bind(&self.active_panel, next_index)?;
119        statement.bind(&self.zoom, next_index)
120    }
121}
122
123#[derive(Debug, PartialEq, Clone)]
124pub(crate) enum SerializedPaneGroup {
125    Group {
126        axis: SerializedAxis,
127        flexes: Option<Vec<f32>>,
128        children: Vec<SerializedPaneGroup>,
129    },
130    Pane(SerializedPane),
131}
132
133#[cfg(test)]
134impl Default for SerializedPaneGroup {
135    fn default() -> Self {
136        Self::Pane(SerializedPane {
137            children: vec![SerializedItem::default()],
138            active: false,
139            pinned_count: 0,
140        })
141    }
142}
143
144impl SerializedPaneGroup {
145    #[async_recursion(?Send)]
146    pub(crate) async fn deserialize(
147        self,
148        project: &Entity<Project>,
149        workspace_id: WorkspaceId,
150        workspace: WeakEntity<Workspace>,
151        cx: &mut AsyncWindowContext,
152    ) -> Option<(
153        Member,
154        Option<Entity<Pane>>,
155        Vec<Option<Box<dyn ItemHandle>>>,
156    )> {
157        match self {
158            SerializedPaneGroup::Group {
159                axis,
160                children,
161                flexes,
162            } => {
163                let mut current_active_pane = None;
164                let mut members = Vec::new();
165                let mut items = Vec::new();
166                for child in children {
167                    if let Some((new_member, active_pane, new_items)) = child
168                        .deserialize(project, workspace_id, workspace.clone(), cx)
169                        .await
170                    {
171                        members.push(new_member);
172                        items.extend(new_items);
173                        current_active_pane = current_active_pane.or(active_pane);
174                    }
175                }
176
177                if members.is_empty() {
178                    return None;
179                }
180
181                if members.len() == 1 {
182                    return Some((members.remove(0), current_active_pane, items));
183                }
184
185                Some((
186                    Member::Axis(PaneAxis::load(axis.0, members, flexes)),
187                    current_active_pane,
188                    items,
189                ))
190            }
191            SerializedPaneGroup::Pane(serialized_pane) => {
192                let pane = workspace
193                    .update_in(cx, |workspace, window, cx| {
194                        workspace.add_pane(window, cx).downgrade()
195                    })
196                    .log_err()?;
197                let active = serialized_pane.active;
198                let new_items = serialized_pane
199                    .deserialize_to(project, &pane, workspace_id, workspace.clone(), cx)
200                    .await
201                    .log_err()?;
202
203                if pane
204                    .read_with(cx, |pane, _| pane.items_len() != 0)
205                    .log_err()?
206                {
207                    let pane = pane.upgrade()?;
208                    Some((
209                        Member::Pane(pane.clone()),
210                        active.then_some(pane),
211                        new_items,
212                    ))
213                } else {
214                    let pane = pane.upgrade()?;
215                    workspace
216                        .update_in(cx, |workspace, window, cx| {
217                            workspace.force_remove_pane(&pane, &None, window, cx)
218                        })
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    pub(crate) pinned_count: usize,
232}
233
234impl SerializedPane {
235    pub fn new(children: Vec<SerializedItem>, active: bool, pinned_count: usize) -> Self {
236        SerializedPane {
237            children,
238            active,
239            pinned_count,
240        }
241    }
242
243    pub async fn deserialize_to(
244        &self,
245        project: &Entity<Project>,
246        pane: &WeakEntity<Pane>,
247        workspace_id: WorkspaceId,
248        workspace: WeakEntity<Workspace>,
249        cx: &mut AsyncWindowContext,
250    ) -> Result<Vec<Option<Box<dyn ItemHandle>>>> {
251        let mut item_tasks = Vec::new();
252        let mut active_item_index = None;
253        let mut preview_item_index = None;
254        for (index, item) in self.children.iter().enumerate() {
255            let project = project.clone();
256            item_tasks.push(pane.update_in(cx, |_, window, cx| {
257                SerializableItemRegistry::deserialize(
258                    &item.kind,
259                    project,
260                    workspace.clone(),
261                    workspace_id,
262                    item.item_id,
263                    window,
264                    cx,
265                )
266            })?);
267            if item.active {
268                active_item_index = Some(index);
269            }
270            if item.preview {
271                preview_item_index = Some(index);
272            }
273        }
274
275        let mut items = Vec::new();
276        for item_handle in futures::future::join_all(item_tasks).await {
277            let item_handle = item_handle.log_err();
278            items.push(item_handle.clone());
279
280            if let Some(item_handle) = item_handle {
281                pane.update_in(cx, |pane, window, cx| {
282                    pane.add_item(item_handle.clone(), true, true, None, window, cx);
283                })?;
284            }
285        }
286
287        if let Some(active_item_index) = active_item_index {
288            pane.update_in(cx, |pane, window, cx| {
289                pane.activate_item(active_item_index, false, false, window, cx);
290            })?;
291        }
292
293        if let Some(preview_item_index) = preview_item_index {
294            pane.update(cx, |pane, cx| {
295                if let Some(item) = pane.item_for_index(preview_item_index) {
296                    pane.set_preview_item_id(Some(item.item_id()), cx);
297                }
298            })?;
299        }
300        pane.update(cx, |pane, _| {
301            pane.set_pinned_count(self.pinned_count.min(items.len()));
302        })?;
303
304        anyhow::Ok(items)
305    }
306}
307
308pub type GroupId = i64;
309pub type PaneId = i64;
310pub type ItemId = u64;
311
312#[derive(Debug, PartialEq, Eq, Clone)]
313pub struct SerializedItem {
314    pub kind: Arc<str>,
315    pub item_id: ItemId,
316    pub active: bool,
317    pub preview: bool,
318}
319
320impl SerializedItem {
321    pub fn new(kind: impl AsRef<str>, item_id: ItemId, active: bool, preview: bool) -> Self {
322        Self {
323            kind: Arc::from(kind.as_ref()),
324            item_id,
325            active,
326            preview,
327        }
328    }
329}
330
331#[cfg(test)]
332impl Default for SerializedItem {
333    fn default() -> Self {
334        SerializedItem {
335            kind: Arc::from("Terminal"),
336            item_id: 100000,
337            active: false,
338            preview: false,
339        }
340    }
341}
342
343impl StaticColumnCount for SerializedItem {
344    fn column_count() -> usize {
345        4
346    }
347}
348impl Bind for &SerializedItem {
349    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
350        let next_index = statement.bind(&self.kind, start_index)?;
351        let next_index = statement.bind(&self.item_id, next_index)?;
352        let next_index = statement.bind(&self.active, next_index)?;
353        statement.bind(&self.preview, next_index)
354    }
355}
356
357impl Column for SerializedItem {
358    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
359        let (kind, next_index) = Arc::<str>::column(statement, start_index)?;
360        let (item_id, next_index) = ItemId::column(statement, next_index)?;
361        let (active, next_index) = bool::column(statement, next_index)?;
362        let (preview, next_index) = bool::column(statement, next_index)?;
363        Ok((
364            SerializedItem {
365                kind,
366                item_id,
367                active,
368                preview,
369            },
370            next_index,
371        ))
372    }
373}