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