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