Started working on dock panes

Mikayla Maki and kay@zed.dev created

co-authored-by: kay@zed.dev

Change summary

crates/db/examples/serialize-pane.rs |  22 +++-
crates/db/src/pane.rs                | 158 +++++++++++++++++------------
crates/db/src/workspace.rs           |   3 
3 files changed, 109 insertions(+), 74 deletions(-)

Detailed changes

crates/db/examples/serialize-pane.rs 🔗

@@ -14,15 +14,25 @@ fn main() -> anyhow::Result<()> {
     let f = File::create(file)?;
     drop(f);
 
-    let workspace = db.workspace_for_roots(&["/tmp"]);
+    let workspace_1 = db.workspace_for_roots(&["/tmp"]);
+    let workspace_2 = db.workspace_for_roots(&["/tmp", "/tmp2"]);
+    let workspace_3 = db.workspace_for_roots(&["/tmp3", "/tmp2"]);
 
-    db.save_dock_pane(SerializedDockPane {
-        workspace: workspace.workspace_id,
+    db.save_dock_pane(&SerializedDockPane {
+        workspace_id: workspace_1.workspace_id,
         anchor_position: DockAnchor::Expanded,
-        shown: true,
+        visible: true,
+    });
+    db.save_dock_pane(&SerializedDockPane {
+        workspace_id: workspace_2.workspace_id,
+        anchor_position: DockAnchor::Bottom,
+        visible: true,
+    });
+    db.save_dock_pane(&SerializedDockPane {
+        workspace_id: workspace_3.workspace_id,
+        anchor_position: DockAnchor::Right,
+        visible: false,
     });
-
-    let _new_workspace = db.workspace_for_roots(&["/tmp"]);
 
     db.write_to(file).ok();
 

crates/db/src/pane.rs 🔗

@@ -1,9 +1,69 @@
 use gpui::Axis;
 
+use serde::{Deserialize, Serialize};
+use serde_rusqlite::to_params_named;
+
 use crate::{items::ItemId, workspace::WorkspaceId};
 
 use super::Db;
 
+pub(crate) const PANE_M_1: &str = "
+CREATE TABLE dock_panes(
+    dock_pane_id INTEGER PRIMARY KEY,
+    workspace_id INTEGER NOT NULL,
+    anchor_position TEXT NOT NULL, -- Enum: 'Bottom' / 'Right' / 'Expanded'
+    visible INTEGER NOT NULL, -- Boolean
+    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE
+) STRICT;
+
+CREATE TABLE pane_groups(
+    group_id INTEGER PRIMARY KEY,
+    workspace_id INTEGER NOT NULL,
+    parent_group INTEGER, -- NULL indicates that this is a root node
+    axis TEXT NOT NULL, -- Enum:  'Vertical' / 'Horizontal'
+    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
+    FOREIGN KEY(parent_group) REFERENCES pane_groups(group_id) ON DELETE CASCADE
+) STRICT;
+
+CREATE TABLE grouped_panes(
+    pane_id INTEGER PRIMARY KEY,
+    workspace_id INTEGER NOT NULL,
+    group_id INTEGER NOT NULL,
+    idx INTEGER NOT NULL,
+    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
+    FOREIGN KEY(group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE
+) STRICT;
+
+CREATE TABLE items(
+    item_id INTEGER PRIMARY KEY,
+    workspace_id INTEGER NOT NULL,
+    kind TEXT NOT NULL,
+    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE
+) STRICT;
+
+CREATE TABLE group_items(
+    workspace_id INTEGER NOT NULL,
+    pane_id INTEGER NOT NULL,
+    item_id INTEGER NOT NULL,
+    idx INTEGER NOT NULL,
+    PRIMARY KEY (workspace_id, pane_id, item_id)
+    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
+    FOREIGN KEY(pane_id) REFERENCES grouped_panes(pane_id) ON DELETE CASCADE,
+    FOREIGN KEY(item_id) REFERENCES items(item_id) ON DELETE CASCADE
+) STRICT;
+
+CREATE TABLE dock_items(
+    workspace_id INTEGER NOT NULL,
+    dock_pane_id INTEGER NOT NULL,
+    item_id INTEGER NOT NULL,
+    idx INTEGER NOT NULL,
+    PRIMARY KEY (workspace_id, dock_pane_id, item_id)
+    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
+    FOREIGN KEY(dock_pane_id) REFERENCES dock_panes(dock_pane_id) ON DELETE CASCADE,
+    FOREIGN KEY(item_id) REFERENCES items(item_id)ON DELETE CASCADE
+) STRICT;
+";
+
 // We have an many-branched, unbalanced tree with three types:
 // Pane Groups
 // Panes
@@ -74,64 +134,7 @@ pub struct SerializedPane {
     children: Vec<ItemId>,
 }
 
-pub(crate) const PANE_M_1: &str = "
-CREATE TABLE dock_panes(
-    dock_pane_id INTEGER PRIMARY KEY,
-    workspace_id INTEGER NOT NULL,
-    anchor_position TEXT NOT NULL, -- Enum: 'Bottom' / 'Right' / 'Expanded'
-    shown INTEGER NOT NULL, -- Boolean
-    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE
-) STRICT;
-
-CREATE TABLE pane_groups(
-    group_id INTEGER PRIMARY KEY,
-    workspace_id INTEGER NOT NULL,
-    parent_group INTEGER, -- NULL indicates that this is a root node
-    axis TEXT NOT NULL, -- Enum:  'Vertical' / 'Horizontal'
-    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
-    FOREIGN KEY(parent_group) REFERENCES pane_groups(group_id) ON DELETE CASCADE
-) STRICT;
-
-CREATE TABLE grouped_panes(
-    pane_id INTEGER PRIMARY KEY,
-    workspace_id INTEGER NOT NULL,
-    group_id INTEGER NOT NULL,
-    idx INTEGER NOT NULL,
-    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
-    FOREIGN KEY(group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE
-) STRICT;
-
-CREATE TABLE items(
-    item_id INTEGER PRIMARY KEY,
-    workspace_id INTEGER NOT NULL,
-    kind TEXT NOT NULL,
-    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE
-) STRICT;
-
-CREATE TABLE group_items(
-    workspace_id INTEGER NOT NULL,
-    pane_id INTEGER NOT NULL,
-    item_id INTEGER NOT NULL,
-    idx INTEGER NOT NULL,
-    PRIMARY KEY (workspace_id, pane_id, item_id)
-    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
-    FOREIGN KEY(pane_id) REFERENCES grouped_panes(pane_id) ON DELETE CASCADE,
-    FOREIGN KEY(item_id) REFERENCES items(item_id) ON DELETE CASCADE
-) STRICT;
-
-CREATE TABLE dock_items(
-    workspace_id INTEGER NOT NULL,
-    dock_pane_id INTEGER NOT NULL,
-    item_id INTEGER NOT NULL,
-    idx INTEGER NOT NULL,
-    PRIMARY KEY (workspace_id, dock_pane_id, item_id)
-    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE,
-    FOREIGN KEY(dock_pane_id) REFERENCES dock_panes(dock_pane_id) ON DELETE CASCADE,
-    FOREIGN KEY(item_id) REFERENCES items(item_id)ON DELETE CASCADE
-) STRICT;
-";
-
-#[derive(Default, Debug)]
+#[derive(Default, Debug, PartialEq, Eq, Deserialize, Serialize)]
 pub enum DockAnchor {
     #[default]
     Bottom,
@@ -139,11 +142,11 @@ pub enum DockAnchor {
     Expanded,
 }
 
-#[derive(Default, Debug)]
+#[derive(Default, Debug, PartialEq, Eq, Deserialize, Serialize)]
 pub struct SerializedDockPane {
-    pub workspace: WorkspaceId,
+    pub workspace_id: WorkspaceId,
     pub anchor_position: DockAnchor,
-    pub shown: bool,
+    pub visible: bool,
 }
 
 impl Db {
@@ -204,7 +207,24 @@ impl Db {
         unimplemented!()
     }
 
-    pub fn save_dock_pane(&self, _dock_pane: SerializedDockPane) {}
+    pub fn save_dock_pane(&self, dock_pane: &SerializedDockPane) {
+        to_params_named(dock_pane)
+            .map_err(|err| dbg!(err))
+            .ok()
+            .zip(self.real())
+            .map(|(params, db)| {
+                // TODO: overwrite old dock panes if need be
+                let query = "INSERT INTO dock_panes (workspace_id, anchor_position, visible) VALUES (:workspace_id, :anchor_position, :visible);";
+                db.connection
+                    .lock()
+                    .execute(query, params.to_slice().as_slice())
+                    .map(|_| ()) // Eat the return value
+                    .unwrap_or_else(|err| {
+                        dbg!(&err);
+                        log::error!("Failed to insert new workspace into DB: {}", err);
+                    })
+            });
+    }
 }
 
 #[cfg(test)]
@@ -220,12 +240,16 @@ mod tests {
 
         let workspace = db.workspace_for_roots(&["/tmp"]);
 
-        db.save_dock_pane(SerializedDockPane {
-            workspace: workspace.workspace_id,
+        let dock_pane = SerializedDockPane {
+            workspace_id: workspace.workspace_id,
             anchor_position: DockAnchor::Expanded,
-            shown: true,
-        });
+            visible: true,
+        };
+
+        db.save_dock_pane(&dock_pane);
+
+        let new_workspace = db.workspace_for_roots(&["/tmp"]);
 
-        let _new_workspace = db.workspace_for_roots(&["/tmp"]);
+        assert_eq!(new_workspace.dock_pane.unwrap(), dock_pane);
     }
 }

crates/db/src/workspace.rs 🔗

@@ -1,5 +1,6 @@
 use anyhow::Result;
 use rusqlite::{params, Connection, OptionalExtension};
+use serde::{Deserialize, Serialize};
 
 use std::{
     ffi::OsStr,
@@ -30,7 +31,7 @@ CREATE TABLE worktree_roots(
 ) STRICT;
 ";
 
-#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
+#[derive(Debug, PartialEq, Eq, Copy, Clone, Default, Deserialize, Serialize)]
 pub struct WorkspaceId(i64);
 
 #[derive(Default, Debug)]