Moved to workspaces crate... don't feel great about it

This commit is contained in:
Mikayla Maki 2022-11-10 21:08:20 -08:00
parent e578f2530e
commit a5edac312e
12 changed files with 128 additions and 97 deletions

5
Cargo.lock generated
View file

@ -1040,6 +1040,7 @@ dependencies = [
"client", "client",
"collections", "collections",
"ctor", "ctor",
"db",
"editor", "editor",
"env_logger", "env_logger",
"envy", "envy",
@ -1550,7 +1551,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
"bincode",
"collections", "collections",
"env_logger", "env_logger",
"gpui", "gpui",
@ -7620,6 +7620,7 @@ name = "workspace"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode",
"call", "call",
"client", "client",
"collections", "collections",
@ -7629,6 +7630,7 @@ dependencies = [
"fs", "fs",
"futures 0.3.25", "futures 0.3.25",
"gpui", "gpui",
"indoc",
"language", "language",
"log", "log",
"menu", "menu",
@ -7639,6 +7641,7 @@ dependencies = [
"serde_json", "serde_json",
"settings", "settings",
"smallvec", "smallvec",
"sqlez",
"theme", "theme",
"util", "util",
] ]

View file

@ -18,6 +18,7 @@ live_kit_server = { path = "../live_kit_server" }
rpc = { path = "../rpc" } rpc = { path = "../rpc" }
util = { path = "../util" } util = { path = "../util" }
db = { path = "../db" }
anyhow = "1.0.40" anyhow = "1.0.40"
async-trait = "0.1.50" async-trait = "0.1.50"
async-tungstenite = "0.16" async-tungstenite = "0.16"

View file

@ -1,8 +1,9 @@
use crate::{ use crate::{
db::{NewUserParams, ProjectId, SqliteTestDb as TestDb, UserId}, db::{Db, NewUserParams, ProjectId, UserId},
rpc::{Executor, Server}, rpc::{Executor, Server},
AppState, AppState,
}; };
use ::rpc::Peer; use ::rpc::Peer;
use anyhow::anyhow; use anyhow::anyhow;
use call::{room, ActiveCall, ParticipantLocation, Room}; use call::{room, ActiveCall, ParticipantLocation, Room};
@ -11,6 +12,7 @@ use client::{
User, UserStore, RECEIVE_TIMEOUT, User, UserStore, RECEIVE_TIMEOUT,
}; };
use collections::{BTreeMap, HashMap, HashSet}; use collections::{BTreeMap, HashMap, HashSet};
use db as SqliteDb;
use editor::{ use editor::{
self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Redo, Rename, ToOffset, self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Redo, Rename, ToOffset,
ToggleCodeActions, Undo, ToggleCodeActions, Undo,
@ -5836,7 +5838,11 @@ impl TestServer {
Project::init(&client); Project::init(&client);
cx.update(|cx| { cx.update(|cx| {
workspace::init(app_state.clone(), cx); workspace::init(
app_state.clone(),
cx,
SqliteDb::open_in_memory("integration tests"),
);
call::init(client.clone(), user_store.clone(), cx); call::init(client.clone(), user_store.clone(), cx);
}); });

View file

@ -9,11 +9,11 @@ mod db_tests;
#[cfg(test)] #[cfg(test)]
mod integration_tests; mod integration_tests;
use crate::db::{Db, PostgresDb};
use crate::rpc::ResultExt as _; use crate::rpc::ResultExt as _;
use anyhow::anyhow; use anyhow::anyhow;
use axum::{routing::get, Router}; use axum::{routing::get, Router};
use collab::{Error, Result}; use collab::{Error, Result};
use db::DefaultDb as Db;
use serde::Deserialize; use serde::Deserialize;
use std::{ use std::{
env::args, env::args,

View file

@ -320,7 +320,7 @@ mod tests {
use super::*; use super::*;
use editor::Editor; use editor::Editor;
use gpui::TestAppContext; use gpui::TestAppContext;
use project::Project; use project::{Db, Project};
use workspace::{AppState, Workspace}; use workspace::{AppState, Workspace};
#[test] #[test]
@ -345,7 +345,7 @@ mod tests {
cx.update(|cx| { cx.update(|cx| {
editor::init(cx); editor::init(cx);
workspace::init(app_state.clone(), cx); workspace::init(app_state.clone(), cx, Db::open_in_memory("test"));
init(cx); init(cx);
}); });

View file

@ -22,7 +22,6 @@ lazy_static = "1.4.0"
log = { version = "0.4.16", features = ["kv_unstable_serde"] } log = { version = "0.4.16", features = ["kv_unstable_serde"] }
parking_lot = "0.11.1" parking_lot = "0.11.1"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
bincode = "1.2.1"
[dev-dependencies] [dev-dependencies]

View file

@ -1,6 +1,6 @@
use crate::connection::Connection; use crate::connection::Connection;
pub trait Domain: Send + Sync + Clone { pub trait Domain {
fn migrate(conn: &Connection) -> anyhow::Result<()>; fn migrate(conn: &Connection) -> anyhow::Result<()>;
} }

View file

@ -30,8 +30,10 @@ language = { path = "../language" }
menu = { path = "../menu" } menu = { path = "../menu" }
project = { path = "../project" } project = { path = "../project" }
settings = { path = "../settings" } settings = { path = "../settings" }
sqlez = { path = "../sqlez" }
theme = { path = "../theme" } theme = { path = "../theme" }
util = { path = "../util" } util = { path = "../util" }
bincode = "1.2.1"
anyhow = "1.0.38" anyhow = "1.0.38"
futures = "0.3" futures = "0.3"
log = { version = "0.4.16", features = ["kv_unstable_serde"] } log = { version = "0.4.16", features = ["kv_unstable_serde"] }
@ -40,6 +42,8 @@ postage = { version = "0.4.1", features = ["futures-traits"] }
serde = { version = "1.0", features = ["derive", "rc"] } serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = { version = "1.0", features = ["preserve_order"] } serde_json = { version = "1.0", features = ["preserve_order"] }
smallvec = { version = "1.6", features = ["union"] } smallvec = { version = "1.6", features = ["union"] }
indoc = "1.0.4"
[dev-dependencies] [dev-dependencies]
call = { path = "../call", features = ["test-support"] } call = { path = "../call", features = ["test-support"] }

View file

@ -12,6 +12,7 @@ mod status_bar;
mod toolbar; mod toolbar;
mod workspace_db; mod workspace_db;
use crate::workspace_db::model::SerializedWorkspace;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use call::ActiveCall; use call::ActiveCall;
use client::{proto, Client, PeerId, TypedEnvelope, UserStore}; use client::{proto, Client, PeerId, TypedEnvelope, UserStore};
@ -62,8 +63,6 @@ use theme::{Theme, ThemeRegistry};
pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
use util::ResultExt; use util::ResultExt;
use crate::workspace_db::model;
type ProjectItemBuilders = HashMap< type ProjectItemBuilders = HashMap<
TypeId, TypeId,
fn(ModelHandle<Project>, AnyModelHandle, &mut ViewContext<Pane>) -> Box<dyn ItemHandle>, fn(ModelHandle<Project>, AnyModelHandle, &mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
@ -166,7 +165,9 @@ impl_internal_actions!(
); );
impl_actions!(workspace, [ActivatePane]); impl_actions!(workspace, [ActivatePane]);
pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) { pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext, db: Db<Workspace>) {
cx.set_global(db);
pane::init(cx); pane::init(cx);
dock::init(cx); dock::init(cx);
@ -1123,7 +1124,7 @@ enum FollowerItem {
impl Workspace { impl Workspace {
pub fn new( pub fn new(
_serialized_workspace: Option<isize>, _serialized_workspace: Option<SerializedWorkspace>,
project: ModelHandle<Project>, project: ModelHandle<Project>,
dock_default_factory: DefaultItemFactory, dock_default_factory: DefaultItemFactory,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
@ -1291,9 +1292,10 @@ impl Workspace {
// Use the resolved worktree roots to get the serialized_db from the database // Use the resolved worktree roots to get the serialized_db from the database
let serialized_workspace = cx.read(|cx| { let serialized_workspace = cx.read(|cx| {
cx.global::<Db<KeyValue>>() Workspace::workspace_for_roots(
.open_as::<model::Workspace>() cx.global::<Db<Workspace>>(),
.workspace_for_roots(&Vec::from_iter(worktree_roots.into_iter())[..]) &Vec::from_iter(worktree_roots.into_iter())[..],
)
}); });
// Use the serialized workspace to construct the new window // Use the serialized workspace to construct the new window

View file

@ -1,18 +1,20 @@
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use db::Db;
use util::{iife, unzip_option, ResultExt}; use util::{iife, unzip_option, ResultExt};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use indoc::indoc; use indoc::indoc;
use sqlez::{domain::Domain, migrations::Migration}; use sqlez::{connection::Connection, domain::Domain, migrations::Migration};
use super::Workspace;
use self::model::{ use self::model::{
Axis, GroupId, PaneId, SerializedItem, SerializedItemKind, SerializedPane, SerializedPaneGroup, Axis, GroupId, PaneId, SerializedItem, SerializedItemKind, SerializedPane, SerializedPaneGroup,
SerializedWorkspace, WorkspaceId, SerializedWorkspace, WorkspaceId,
}; };
use super::Db;
// 1) Move all of this into Workspace crate // 1) Move all of this into Workspace crate
// 2) Deserialize items fully // 2) Deserialize items fully
// 3) Typed prepares (including how you expect to pull data out) // 3) Typed prepares (including how you expect to pull data out)
@ -70,23 +72,20 @@ pub(crate) const ITEM_MIGRATIONS: Migration = Migration::new(
"}], "}],
); );
#[derive(Clone)]
pub enum Workspace {}
impl Domain for Workspace { impl Domain for Workspace {
fn migrate(conn: &sqlez::connection::Connection) -> anyhow::Result<()> { fn migrate(conn: &Connection) -> anyhow::Result<()> {
WORKSPACES_MIGRATION.run(&conn)?; WORKSPACES_MIGRATION.run(&conn)?;
PANE_MIGRATIONS.run(&conn)?; PANE_MIGRATIONS.run(&conn)?;
ITEM_MIGRATIONS.run(&conn) ITEM_MIGRATIONS.run(&conn)
} }
} }
impl Db<Workspace> { impl Workspace {
/// Returns a serialized workspace for the given worktree_roots. If the passed array /// Returns a serialized workspace for the given worktree_roots. If the passed array
/// is empty, the most recent workspace is returned instead. If no workspace for the /// is empty, the most recent workspace is returned instead. If no workspace for the
/// passed roots is stored, returns none. /// passed roots is stored, returns none.
pub fn workspace_for_roots<P: AsRef<Path>>( pub fn workspace_for_roots<P: AsRef<Path>>(
&self, db: &Db<Workspace>,
worktree_roots: &[P], worktree_roots: &[P],
) -> Option<SerializedWorkspace> { ) -> Option<SerializedWorkspace> {
let workspace_id: WorkspaceId = worktree_roots.into(); let workspace_id: WorkspaceId = worktree_roots.into();
@ -95,12 +94,12 @@ impl Db<Workspace> {
// and we've grabbed the most recent workspace // and we've grabbed the most recent workspace
let (workspace_id, dock_anchor, dock_visible) = iife!({ let (workspace_id, dock_anchor, dock_visible) = iife!({
if worktree_roots.len() == 0 { if worktree_roots.len() == 0 {
self.select_row(indoc! {" db.select_row(indoc! {"
SELECT workspace_id, dock_anchor, dock_visible SELECT workspace_id, dock_anchor, dock_visible
FROM workspaces FROM workspaces
ORDER BY timestamp DESC LIMIT 1"})?()? ORDER BY timestamp DESC LIMIT 1"})?()?
} else { } else {
self.select_row_bound(indoc! {" db.select_row_bound(indoc! {"
SELECT workspace_id, dock_anchor, dock_visible SELECT workspace_id, dock_anchor, dock_visible
FROM workspaces FROM workspaces
WHERE workspace_id = ?"})?(&workspace_id)? WHERE workspace_id = ?"})?(&workspace_id)?
@ -111,12 +110,10 @@ impl Db<Workspace> {
.flatten()?; .flatten()?;
Some(SerializedWorkspace { Some(SerializedWorkspace {
dock_pane: self dock_pane: Workspace::get_dock_pane(&db, &workspace_id)
.get_dock_pane(&workspace_id)
.context("Getting dock pane") .context("Getting dock pane")
.log_err()?, .log_err()?,
center_group: self center_group: Workspace::get_center_pane_group(&db, &workspace_id)
.get_center_pane_group(&workspace_id)
.context("Getting center group") .context("Getting center group")
.log_err()?, .log_err()?,
dock_anchor, dock_anchor,
@ -127,32 +124,32 @@ impl Db<Workspace> {
/// Saves a workspace using the worktree roots. Will garbage collect any workspaces /// Saves a workspace using the worktree roots. Will garbage collect any workspaces
/// that used this workspace previously /// that used this workspace previously
pub fn save_workspace<P: AsRef<Path>>( pub fn save_workspace<P: AsRef<Path>>(
&self, db: &Db<Workspace>,
worktree_roots: &[P], worktree_roots: &[P],
old_roots: Option<&[P]>, old_roots: Option<&[P]>,
workspace: &SerializedWorkspace, workspace: &SerializedWorkspace,
) { ) {
let workspace_id: WorkspaceId = worktree_roots.into(); let workspace_id: WorkspaceId = worktree_roots.into();
self.with_savepoint("update_worktrees", || { db.with_savepoint("update_worktrees", || {
if let Some(old_roots) = old_roots { if let Some(old_roots) = old_roots {
let old_id: WorkspaceId = old_roots.into(); let old_id: WorkspaceId = old_roots.into();
self.exec_bound("DELETE FROM WORKSPACES WHERE workspace_id = ?")?(&old_id)?; db.exec_bound("DELETE FROM WORKSPACES WHERE workspace_id = ?")?(&old_id)?;
} }
// Delete any previous workspaces with the same roots. This cascades to all // Delete any previous workspaces with the same roots. This cascades to all
// other tables that are based on the same roots set. // other tables that are based on the same roots set.
// Insert new workspace into workspaces table if none were found // Insert new workspace into workspaces table if none were found
self.exec_bound("DELETE FROM workspaces WHERE workspace_id = ?;")?(&workspace_id)?; db.exec_bound("DELETE FROM workspaces WHERE workspace_id = ?;")?(&workspace_id)?;
self.exec_bound( db.exec_bound(
"INSERT INTO workspaces(workspace_id, dock_anchor, dock_visible) VALUES (?, ?, ?)", "INSERT INTO workspaces(workspace_id, dock_anchor, dock_visible) VALUES (?, ?, ?)",
)?((&workspace_id, workspace.dock_anchor, workspace.dock_visible))?; )?((&workspace_id, workspace.dock_anchor, workspace.dock_visible))?;
// Save center pane group and dock pane // Save center pane group and dock pane
self.save_pane_group(&workspace_id, &workspace.center_group, None)?; Workspace::save_pane_group(db, &workspace_id, &workspace.center_group, None)?;
self.save_pane(&workspace_id, &workspace.dock_pane, None)?; Workspace::save_pane(db, &workspace_id, &workspace.dock_pane, None)?;
Ok(()) Ok(())
}) })
@ -169,11 +166,11 @@ impl Db<Workspace> {
} }
/// Returns the previous workspace ids sorted by last modified along with their opened worktree roots /// Returns the previous workspace ids sorted by last modified along with their opened worktree roots
pub fn recent_workspaces(&self, limit: usize) -> Vec<Vec<PathBuf>> { pub fn recent_workspaces(conn: &Connection, limit: usize) -> Vec<Vec<PathBuf>> {
iife!({ iife!({
// TODO, upgrade anyhow: https://docs.rs/anyhow/1.0.66/anyhow/fn.Ok.html // TODO, upgrade anyhow: https://docs.rs/anyhow/1.0.66/anyhow/fn.Ok.html
Ok::<_, anyhow::Error>( Ok::<_, anyhow::Error>(
self.select_bound::<usize, WorkspaceId>( conn.select_bound::<usize, WorkspaceId>(
"SELECT workspace_id FROM workspaces ORDER BY timestamp DESC LIMIT ?", "SELECT workspace_id FROM workspaces ORDER BY timestamp DESC LIMIT ?",
)?(limit)? )?(limit)?
.into_iter() .into_iter()
@ -186,21 +183,21 @@ impl Db<Workspace> {
} }
pub(crate) fn get_center_pane_group( pub(crate) fn get_center_pane_group(
&self, db: &Db<Workspace>,
workspace_id: &WorkspaceId, workspace_id: &WorkspaceId,
) -> Result<SerializedPaneGroup> { ) -> Result<SerializedPaneGroup> {
self.get_pane_group_children(workspace_id, None)? Workspace::get_pane_group_children(&db, workspace_id, None)?
.into_iter() .into_iter()
.next() .next()
.context("No center pane group") .context("No center pane group")
} }
fn get_pane_group_children<'a>( fn get_pane_group_children<'a>(
&self, db: &Db<Workspace>,
workspace_id: &WorkspaceId, workspace_id: &WorkspaceId,
group_id: Option<GroupId>, group_id: Option<GroupId>,
) -> Result<Vec<SerializedPaneGroup>> { ) -> Result<Vec<SerializedPaneGroup>> {
self.select_bound::<(Option<GroupId>, &WorkspaceId), (Option<GroupId>, Option<Axis>, Option<PaneId>)>(indoc! {" db.select_bound::<(Option<GroupId>, &WorkspaceId), (Option<GroupId>, Option<Axis>, Option<PaneId>)>(indoc! {"
SELECT group_id, axis, pane_id SELECT group_id, axis, pane_id
FROM (SELECT group_id, axis, NULL as pane_id, position, parent_group_id, workspace_id FROM (SELECT group_id, axis, NULL as pane_id, position, parent_group_id, workspace_id
FROM pane_groups FROM pane_groups
@ -217,14 +214,15 @@ impl Db<Workspace> {
if let Some((group_id, axis)) = group_id.zip(axis) { if let Some((group_id, axis)) = group_id.zip(axis) {
Ok(SerializedPaneGroup::Group { Ok(SerializedPaneGroup::Group {
axis, axis,
children: self.get_pane_group_children( children: Workspace::get_pane_group_children(
db,
workspace_id, workspace_id,
Some(group_id), Some(group_id),
)?, )?,
}) })
} else if let Some(pane_id) = pane_id { } else if let Some(pane_id) = pane_id {
Ok(SerializedPaneGroup::Pane(SerializedPane { Ok(SerializedPaneGroup::Pane(SerializedPane {
children: self.get_items(pane_id)?, children: Workspace::get_items(db, pane_id)?,
})) }))
} else { } else {
bail!("Pane Group Child was neither a pane group or a pane"); bail!("Pane Group Child was neither a pane group or a pane");
@ -234,7 +232,7 @@ impl Db<Workspace> {
} }
pub(crate) fn save_pane_group( pub(crate) fn save_pane_group(
&self, db: &Db<Workspace>,
workspace_id: &WorkspaceId, workspace_id: &WorkspaceId,
pane_group: &SerializedPaneGroup, pane_group: &SerializedPaneGroup,
parent: Option<(GroupId, usize)>, parent: Option<(GroupId, usize)>,
@ -247,20 +245,28 @@ impl Db<Workspace> {
match pane_group { match pane_group {
SerializedPaneGroup::Group { axis, children } => { SerializedPaneGroup::Group { axis, children } => {
let parent_id = self.insert_bound("INSERT INTO pane_groups(workspace_id, parent_group_id, position, axis) VALUES (?, ?, ?, ?)")? let parent_id = db.insert_bound("INSERT INTO pane_groups(workspace_id, parent_group_id, position, axis) VALUES (?, ?, ?, ?)")?
((workspace_id, parent_id, position, *axis))?; ((workspace_id, parent_id, position, *axis))?;
for (position, group) in children.iter().enumerate() { for (position, group) in children.iter().enumerate() {
self.save_pane_group(workspace_id, group, Some((parent_id, position)))? Workspace::save_pane_group(
db,
workspace_id,
group,
Some((parent_id, position)),
)?
} }
Ok(()) Ok(())
} }
SerializedPaneGroup::Pane(pane) => self.save_pane(workspace_id, pane, parent), SerializedPaneGroup::Pane(pane) => Workspace::save_pane(db, workspace_id, pane, parent),
} }
} }
pub(crate) fn get_dock_pane(&self, workspace_id: &WorkspaceId) -> Result<SerializedPane> { pub(crate) fn get_dock_pane(
let pane_id = self.select_row_bound(indoc! {" db: &Db<Workspace>,
workspace_id: &WorkspaceId,
) -> Result<SerializedPane> {
let pane_id = db.select_row_bound(indoc! {"
SELECT pane_id FROM panes SELECT pane_id FROM panes
WHERE workspace_id = ? AND parent_group_id IS NULL AND position IS NULL"})?( WHERE workspace_id = ? AND parent_group_id IS NULL AND position IS NULL"})?(
workspace_id, workspace_id,
@ -268,28 +274,27 @@ impl Db<Workspace> {
.context("No dock pane for workspace")?; .context("No dock pane for workspace")?;
Ok(SerializedPane::new( Ok(SerializedPane::new(
self.get_items(pane_id).context("Reading items")?, Workspace::get_items(db, pane_id).context("Reading items")?,
)) ))
} }
pub(crate) fn save_pane( pub(crate) fn save_pane(
&self, db: &Db<Workspace>,
workspace_id: &WorkspaceId, workspace_id: &WorkspaceId,
pane: &SerializedPane, pane: &SerializedPane,
parent: Option<(GroupId, usize)>, parent: Option<(GroupId, usize)>,
) -> Result<()> { ) -> Result<()> {
let (parent_id, order) = unzip_option(parent); let (parent_id, order) = unzip_option(parent);
let pane_id = self.insert_bound( let pane_id = db.insert_bound(
"INSERT INTO panes(workspace_id, parent_group_id, position) VALUES (?, ?, ?)", "INSERT INTO panes(workspace_id, parent_group_id, position) VALUES (?, ?, ?)",
)?((workspace_id, parent_id, order))?; )?((workspace_id, parent_id, order))?;
self.save_items(workspace_id, pane_id, &pane.children) Workspace::save_items(db, workspace_id, pane_id, &pane.children).context("Saving items")
.context("Saving items")
} }
pub(crate) fn get_items(&self, pane_id: PaneId) -> Result<Vec<SerializedItem>> { pub(crate) fn get_items(db: &Db<Workspace>, pane_id: PaneId) -> Result<Vec<SerializedItem>> {
Ok(self.select_bound(indoc! {" Ok(db.select_bound(indoc! {"
SELECT item_id, kind FROM items SELECT item_id, kind FROM items
WHERE pane_id = ? WHERE pane_id = ?
ORDER BY position"})?(pane_id)? ORDER BY position"})?(pane_id)?
@ -302,15 +307,15 @@ impl Db<Workspace> {
} }
pub(crate) fn save_items( pub(crate) fn save_items(
&self, db: &Db<Workspace>,
workspace_id: &WorkspaceId, workspace_id: &WorkspaceId,
pane_id: PaneId, pane_id: PaneId,
items: &[SerializedItem], items: &[SerializedItem],
) -> Result<()> { ) -> Result<()> {
let mut delete_old = self let mut delete_old = db
.exec_bound("DELETE FROM items WHERE workspace_id = ? AND pane_id = ? AND item_id = ?") .exec_bound("DELETE FROM items WHERE workspace_id = ? AND pane_id = ? AND item_id = ?")
.context("Preparing deletion")?; .context("Preparing deletion")?;
let mut insert_new = self.exec_bound( let mut insert_new = db.exec_bound(
"INSERT INTO items(item_id, workspace_id, pane_id, kind, position) VALUES (?, ?, ?, ?, ?)", "INSERT INTO items(item_id, workspace_id, pane_id, kind, position) VALUES (?, ?, ?, ?, ?)",
).context("Preparing insertion")?; ).context("Preparing insertion")?;
for (position, item) in items.iter().enumerate() { for (position, item) in items.iter().enumerate() {
@ -324,17 +329,12 @@ impl Db<Workspace> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::workspace_db::model::DockAnchor::{Bottom, Expanded, Right};
model::{ use crate::{Db, Workspace};
DockAnchor::{Bottom, Expanded, Right},
SerializedWorkspace,
},
Db,
};
#[test] #[test]
fn test_workspace_assignment() { fn test_workspace_assignment() {
env_logger::try_init().ok(); // env_logger::try_init().ok();
let db = Db::open_in_memory("test_basic_functionality"); let db = Db::open_in_memory("test_basic_functionality");
@ -359,61 +359,73 @@ mod tests {
dock_pane: Default::default(), dock_pane: Default::default(),
}; };
db.save_workspace(&["/tmp", "/tmp2"], None, &workspace_1); Workspace::save_workspace(&db, &["/tmp", "/tmp2"], None, &workspace_1);
db.save_workspace(&["/tmp"], None, &workspace_2); Workspace::save_workspace(&db, &["/tmp"], None, &workspace_2);
db.write_to("test.db").unwrap(); db.write_to("test.db").unwrap();
// Test that paths are treated as a set // Test that paths are treated as a set
assert_eq!( assert_eq!(
db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(), Workspace::workspace_for_roots(&db, &["/tmp", "/tmp2"]).unwrap(),
workspace_1 workspace_1
); );
assert_eq!( assert_eq!(
db.workspace_for_roots(&["/tmp2", "/tmp"]).unwrap(), Workspace::workspace_for_roots(&db, &["/tmp2", "/tmp"]).unwrap(),
workspace_1 workspace_1
); );
// Make sure that other keys work // Make sure that other keys work
assert_eq!(db.workspace_for_roots(&["/tmp"]).unwrap(), workspace_2); assert_eq!(
assert_eq!(db.workspace_for_roots(&["/tmp3", "/tmp2", "/tmp4"]), None); Workspace::workspace_for_roots(&db, &["/tmp"]).unwrap(),
workspace_2
);
assert_eq!(
Workspace::workspace_for_roots(&db, &["/tmp3", "/tmp2", "/tmp4"]),
None
);
// Test 'mutate' case of updating a pre-existing id // Test 'mutate' case of updating a pre-existing id
db.save_workspace(&["/tmp", "/tmp2"], Some(&["/tmp", "/tmp2"]), &workspace_2); Workspace::save_workspace(
&db,
&["/tmp", "/tmp2"],
Some(&["/tmp", "/tmp2"]),
&workspace_2,
);
assert_eq!( assert_eq!(
db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(), Workspace::workspace_for_roots(&db, &["/tmp", "/tmp2"]).unwrap(),
workspace_2 workspace_2
); );
// Test other mechanism for mutating // Test other mechanism for mutating
db.save_workspace(&["/tmp", "/tmp2"], None, &workspace_3); Workspace::save_workspace(&db, &["/tmp", "/tmp2"], None, &workspace_3);
assert_eq!( assert_eq!(
db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(), Workspace::workspace_for_roots(&db, &["/tmp", "/tmp2"]).unwrap(),
workspace_3 workspace_3
); );
// Make sure that updating paths differently also works // Make sure that updating paths differently also works
db.save_workspace( Workspace::save_workspace(
&db,
&["/tmp3", "/tmp4", "/tmp2"], &["/tmp3", "/tmp4", "/tmp2"],
Some(&["/tmp", "/tmp2"]), Some(&["/tmp", "/tmp2"]),
&workspace_3, &workspace_3,
); );
assert_eq!(db.workspace_for_roots(&["/tmp2", "tmp"]), None); assert_eq!(Workspace::workspace_for_roots(&db, &["/tmp2", "tmp"]), None);
assert_eq!( assert_eq!(
db.workspace_for_roots(&["/tmp2", "/tmp3", "/tmp4"]) Workspace::workspace_for_roots(&db, &["/tmp2", "/tmp3", "/tmp4"]).unwrap(),
.unwrap(),
workspace_3 workspace_3
); );
} }
use crate::model::{SerializedItem, SerializedPane, SerializedPaneGroup}; use crate::workspace_db::model::SerializedWorkspace;
use crate::workspace_db::model::{SerializedItem, SerializedPane, SerializedPaneGroup};
fn default_workspace( fn default_workspace(
dock_pane: SerializedPane, dock_pane: SerializedPane,
center_group: &SerializedPaneGroup, center_group: &SerializedPaneGroup,
) -> SerializedWorkspace { ) -> SerializedWorkspace {
SerializedWorkspace { SerializedWorkspace {
dock_anchor: crate::model::DockAnchor::Right, dock_anchor: crate::workspace_db::model::DockAnchor::Right,
dock_visible: false, dock_visible: false,
center_group: center_group.clone(), center_group: center_group.clone(),
dock_pane, dock_pane,
@ -422,11 +434,11 @@ mod tests {
#[test] #[test]
fn test_basic_dock_pane() { fn test_basic_dock_pane() {
env_logger::try_init().ok(); // env_logger::try_init().ok();
let db = Db::open_in_memory("basic_dock_pane"); let db = Db::open_in_memory("basic_dock_pane");
let dock_pane = crate::model::SerializedPane { let dock_pane = crate::workspace_db::model::SerializedPane {
children: vec![ children: vec![
SerializedItem::Terminal { item_id: 1 }, SerializedItem::Terminal { item_id: 1 },
SerializedItem::Terminal { item_id: 4 }, SerializedItem::Terminal { item_id: 4 },
@ -437,16 +449,16 @@ mod tests {
let workspace = default_workspace(dock_pane, &Default::default()); let workspace = default_workspace(dock_pane, &Default::default());
db.save_workspace(&["/tmp"], None, &workspace); Workspace::save_workspace(&db, &["/tmp"], None, &workspace);
let new_workspace = db.workspace_for_roots(&["/tmp"]).unwrap(); let new_workspace = Workspace::workspace_for_roots(&db, &["/tmp"]).unwrap();
assert_eq!(workspace.dock_pane, new_workspace.dock_pane); assert_eq!(workspace.dock_pane, new_workspace.dock_pane);
} }
#[test] #[test]
fn test_simple_split() { fn test_simple_split() {
env_logger::try_init().ok(); // env_logger::try_init().ok();
let db = Db::open_in_memory("simple_split"); let db = Db::open_in_memory("simple_split");
@ -456,10 +468,10 @@ mod tests {
// | 3,4 | | // | 3,4 | |
// ----------------- // -----------------
let center_pane = SerializedPaneGroup::Group { let center_pane = SerializedPaneGroup::Group {
axis: crate::model::Axis::Horizontal, axis: crate::workspace_db::model::Axis::Horizontal,
children: vec![ children: vec![
SerializedPaneGroup::Group { SerializedPaneGroup::Group {
axis: crate::model::Axis::Vertical, axis: crate::workspace_db::model::Axis::Vertical,
children: vec![ children: vec![
SerializedPaneGroup::Pane(SerializedPane { SerializedPaneGroup::Pane(SerializedPane {
children: vec![ children: vec![
@ -486,7 +498,7 @@ mod tests {
let workspace = default_workspace(Default::default(), &center_pane); let workspace = default_workspace(Default::default(), &center_pane);
db.save_workspace(&["/tmp"], None, &workspace); Workspace::save_workspace(&db, &["/tmp"], None, &workspace);
assert_eq!(workspace.center_group, center_pane); assert_eq!(workspace.center_group, center_pane);
} }
@ -720,7 +732,7 @@ pub mod model {
mod tests { mod tests {
use sqlez::connection::Connection; use sqlez::connection::Connection;
use crate::model::DockAnchor; use crate::workspace_db::model::DockAnchor;
use super::WorkspaceId; use super::WorkspaceId;

View file

@ -170,7 +170,11 @@ fn main() {
client::ZED_SERVER_URL.clone(), client::ZED_SERVER_URL.clone(),
cx, cx,
); );
workspace::init(app_state.clone(), cx);
let workspace_db = cx.global::<Db<project::KeyValue>>().open_as::<Workspace>();
workspace::init(app_state.clone(), cx, workspace_db);
journal::init(app_state.clone(), cx); journal::init(app_state.clone(), cx);
theme_selector::init(app_state.clone(), cx); theme_selector::init(app_state.clone(), cx);
zed::init(&app_state, cx); zed::init(&app_state, cx);

View file

@ -630,7 +630,7 @@ mod tests {
use gpui::{ use gpui::{
executor::Deterministic, AssetSource, MutableAppContext, TestAppContext, ViewHandle, executor::Deterministic, AssetSource, MutableAppContext, TestAppContext, ViewHandle,
}; };
use project::{Project, ProjectPath}; use project::{Db, Project, ProjectPath};
use serde_json::json; use serde_json::json;
use std::{ use std::{
collections::HashSet, collections::HashSet,
@ -1817,7 +1817,7 @@ mod tests {
state.initialize_workspace = initialize_workspace; state.initialize_workspace = initialize_workspace;
state.build_window_options = build_window_options; state.build_window_options = build_window_options;
call::init(app_state.client.clone(), app_state.user_store.clone(), cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
workspace::init(app_state.clone(), cx); workspace::init(app_state.clone(), cx, Db::open_in_memory("test"));
editor::init(cx); editor::init(cx);
pane::init(cx); pane::init(cx);
app_state app_state