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