WIP serializing and deserializing editors

Kay Simmons created

Change summary

crates/editor/src/editor.rs                | 27 ++++++++++++++++
crates/editor/src/items.rs                 | 25 ++++++++-------
crates/editor/src/persistence.rs           | 31 +++++++++++++++---
crates/sqlez/src/thread_safe_connection.rs | 38 ++++++++++++------------
4 files changed, 83 insertions(+), 38 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -83,7 +83,7 @@ use theme::{DiagnosticStyle, Theme};
 use util::{post_inc, ResultExt, TryFutureExt};
 use workspace::{ItemNavHistory, Workspace};
 
-use crate::git::diff_hunk_to_display;
+use crate::{git::diff_hunk_to_display, persistence::DB};
 
 const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
 const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
@@ -1137,6 +1137,31 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) -> Self {
         let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+        if let Some(project) = project.as_ref() {
+            if let Some(file) = buffer
+                .read(cx)
+                .as_singleton()
+                .and_then(|buffer| buffer.read(cx).file())
+                .and_then(|file| file.as_local())
+            {
+                let item_id = cx.weak_handle().id();
+                let workspace_id = project
+                    .read(cx)
+                    .visible_worktrees(cx)
+                    .map(|worktree| worktree.read(cx).abs_path())
+                    .collect::<Vec<_>>()
+                    .into();
+                let path = file.abs_path(cx);
+                dbg!(&path);
+
+                cx.background()
+                    .spawn(async move {
+                        DB.save_path(item_id, workspace_id, path).log_err();
+                    })
+                    .detach();
+            }
+        }
+
         Self::new(EditorMode::Full, buffer, project, None, cx)
     }
 

crates/editor/src/items.rs 🔗

@@ -1,8 +1,3 @@
-use crate::{
-    display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
-    movement::surrounding_word, Anchor, Autoscroll, Editor, Event, ExcerptId, MultiBuffer,
-    MultiBufferSnapshot, NavigationData, ToPoint as _, FORMAT_TIMEOUT,
-};
 use anyhow::{anyhow, Context, Result};
 use futures::FutureExt;
 use gpui::{
@@ -29,6 +24,12 @@ use workspace::{
     ItemId, ItemNavHistory, Pane, StatusItemView, ToolbarItemLocation, Workspace, WorkspaceId,
 };
 
+use crate::{
+    display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
+    movement::surrounding_word, persistence::DB, Anchor, Autoscroll, Editor, Event, ExcerptId,
+    MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _, FORMAT_TIMEOUT,
+};
+
 pub const MAX_TAB_TITLE_LEN: usize = 24;
 
 impl FollowableItem for Editor {
@@ -554,21 +555,21 @@ impl Item for Editor {
     }
 
     fn serialized_item_kind() -> Option<&'static str> {
-        // TODO: Some("Editor")
-        None
+        Some("Editor")
     }
 
     fn deserialize(
         project: ModelHandle<Project>,
         _workspace: WeakViewHandle<Workspace>,
-        _workspace_id: WorkspaceId,
-        _item_id: ItemId,
+        workspace_id: WorkspaceId,
+        item_id: ItemId,
         cx: &mut ViewContext<Pane>,
     ) -> Task<Result<ViewHandle<Self>>> {
-        // Look up the path with this key associated, create a self with that path
-        let path = Path::new(".");
         if let Some(project_item) = project.update(cx, |project, cx| {
-            let (worktree, path) = project.find_local_worktree(path, cx)?;
+            // Look up the path with this key associated, create a self with that path
+            let path = DB.get_path(item_id, workspace_id).ok()?;
+            dbg!(&path);
+            let (worktree, path) = project.find_local_worktree(&path, cx)?;
             let project_path = ProjectPath {
                 worktree_id: worktree.read(cx).id(),
                 path: path.into(),

crates/editor/src/persistence.rs 🔗

@@ -1,11 +1,11 @@
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 
+use anyhow::{Context, Result};
 use db::connection;
 use indoc::indoc;
 use lazy_static::lazy_static;
-use project::WorktreeId;
 use sqlez::domain::Domain;
-use workspace::{ItemId, Workspace};
+use workspace::{ItemId, Workspace, WorkspaceId};
 
 use crate::Editor;
 
@@ -18,13 +18,32 @@ impl Domain for Editor {
 
     fn migrations() -> &'static [&'static str] {
         &[indoc! {"
-                
+            CREATE TABLE editors(
+                item_id INTEGER NOT NULL,
+                workspace_id BLOB NOT NULL,
+                path BLOB NOT NULL,
+                PRIMARY KEY(item_id, workspace_id)
+            ) STRICT;
         "}]
     }
 }
 
 impl EditorDb {
-    fn _get_path(_item_id: ItemId, _workspace_id: WorktreeId) -> PathBuf {
-        unimplemented!();
+    pub fn get_path(&self, item_id: ItemId, workspace_id: WorkspaceId) -> Result<PathBuf> {
+        self.select_row_bound(indoc! {"
+            SELECT path FROM editors 
+            WHERE item_id = ? AND workspace_id = ?"})?((item_id, &workspace_id))?
+        .context("Path not found for serialized editor")
+    }
+
+    pub fn save_path(
+        &self,
+        item_id: ItemId,
+        workspace_id: WorkspaceId,
+        path: PathBuf,
+    ) -> Result<()> {
+        self.exec_bound::<(ItemId, &WorkspaceId, &Path)>(indoc! {"
+            INSERT OR REPLACE INTO editors(item_id, workspace_id, path)
+            VALUES (?, ?, ?)"})?((item_id, &workspace_id, &path))
     }
 }

crates/sqlez/src/thread_safe_connection.rs 🔗

@@ -129,25 +129,25 @@ mod test {
 
             fn migrations() -> &'static [&'static str] {
                 &["
-            CREATE TABLE workspaces(
-                workspace_id BLOB PRIMARY KEY,
-                dock_visible INTEGER, -- Boolean
-                dock_anchor TEXT, -- Enum: 'Bottom' / 'Right' / 'Expanded'
-                dock_pane INTEGER, -- NULL indicates that we don't have a dock pane yet
-                timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
-                FOREIGN KEY(dock_pane) REFERENCES panes(pane_id),
-                FOREIGN KEY(active_pane) REFERENCES panes(pane_id)
-            ) STRICT;
-            
-            CREATE TABLE panes(
-                pane_id INTEGER PRIMARY KEY,
-                workspace_id BLOB NOT NULL,
-                active INTEGER NOT NULL, -- Boolean
-                FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) 
-                    ON DELETE CASCADE 
-                    ON UPDATE CASCADE
-            ) STRICT;
-            "]
+                    CREATE TABLE workspaces(
+                        workspace_id BLOB PRIMARY KEY,
+                        dock_visible INTEGER, -- Boolean
+                        dock_anchor TEXT, -- Enum: 'Bottom' / 'Right' / 'Expanded'
+                        dock_pane INTEGER, -- NULL indicates that we don't have a dock pane yet
+                        timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
+                        FOREIGN KEY(dock_pane) REFERENCES panes(pane_id),
+                        FOREIGN KEY(active_pane) REFERENCES panes(pane_id)
+                    ) STRICT;
+                    
+                    CREATE TABLE panes(
+                        pane_id INTEGER PRIMARY KEY,
+                        workspace_id BLOB NOT NULL,
+                        active INTEGER NOT NULL, -- Boolean
+                        FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) 
+                            ON DELETE CASCADE 
+                            ON UPDATE CASCADE
+                    ) STRICT;
+                "]
             }
         }