Cargo.lock 🔗
@@ -7617,6 +7617,7 @@ dependencies = [
"client",
"collections",
"context_menu",
+ "db",
"drag_and_drop",
"fs",
"futures 0.3.24",
K Simmons created
Cargo.lock | 1
crates/db/Cargo.toml | 1
crates/db/src/db.rs | 1
crates/db/src/items.rs | 62 ++++++++++++++
crates/db/src/pane.rs | 134 ++++++++++++++++++++++++++++++++
crates/db/src/workspace.rs | 137 +++++++++++++++++++++++---------
crates/gpui/src/presenter.rs | 3
crates/workspace/Cargo.toml | 1
crates/workspace/src/dock.rs | 6 +
crates/workspace/src/workspace.rs | 1
10 files changed, 302 insertions(+), 45 deletions(-)
@@ -7617,6 +7617,7 @@ dependencies = [
"client",
"collections",
"context_menu",
+ "db",
"drag_and_drop",
"fs",
"futures 0.3.24",
@@ -12,6 +12,7 @@ test-support = []
[dependencies]
collections = { path = "../collections" }
+gpui = { path = "../gpui" }
anyhow = "1.0.57"
async-trait = "0.1"
lazy_static = "1.4.0"
@@ -1,6 +1,7 @@
mod items;
mod kvp;
mod migrations;
+mod pane;
mod workspace;
use std::fs;
@@ -1,8 +1,17 @@
-use std::{ffi::OsStr, fmt::Display, hash::Hash, os::unix::prelude::OsStrExt, path::PathBuf};
+use std::{
+ ffi::OsStr,
+ fmt::Display,
+ hash::Hash,
+ os::unix::prelude::OsStrExt,
+ path::{Path, PathBuf},
+ sync::Arc,
+};
use anyhow::Result;
use collections::HashSet;
-use rusqlite::{named_params, params};
+use rusqlite::{named_params, params, types::FromSql};
+
+use crate::workspace::WorkspaceId;
use super::Db;
@@ -62,3 +71,52 @@ CREATE TABLE editors(
FOREIGN KEY(workspace_id) REFERENCES workspace_ids(workspace_id)
) STRICT;
";
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct ItemId {
+ workspace_id: usize,
+ item_id: usize,
+}
+
+enum SerializedItemKind {
+ Editor,
+ Diagnostics,
+ ProjectSearch,
+ Terminal,
+}
+
+struct SerializedItemRow {
+ kind: SerializedItemKind,
+ item_id: usize,
+ path: Option<Arc<Path>>,
+ query: Option<String>,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum SerializedItem {
+ Editor { item_id: usize, path: Arc<Path> },
+ Diagnostics { item_id: usize },
+ ProjectSearch { item_id: usize, query: String },
+ Terminal { item_id: usize },
+}
+
+impl SerializedItem {
+ pub fn item_id(&self) -> usize {
+ match self {
+ SerializedItem::Editor { item_id, .. } => *item_id,
+ SerializedItem::Diagnostics { item_id } => *item_id,
+ SerializedItem::ProjectSearch { item_id, .. } => *item_id,
+ SerializedItem::Terminal { item_id } => *item_id,
+ }
+ }
+}
+
+impl Db {
+ pub fn get_item(&self, item_id: ItemId) -> SerializedItem {
+ unimplemented!()
+ }
+
+ pub fn save_item(&self, workspace_id: WorkspaceId, item: &SerializedItem) {}
+
+ pub fn close_item(&self, item_id: ItemId) {}
+}
@@ -0,0 +1,134 @@
+use gpui::Axis;
+
+use crate::{items::ItemId, workspace::WorkspaceId};
+
+use super::Db;
+
+pub(crate) const PANE_M_1: &str = "
+CREATE TABLE pane_groups(
+ workspace_id INTEGER,
+ group_id INTEGER,
+ axis STRING NOT NULL, -- 'Vertical' / 'Horizontal'
+ PRIMARY KEY (workspace_id, group_id)
+) STRICT;
+
+CREATE TABLE pane_group_children(
+ workspace_id INTEGER,
+ group_id INTEGER,
+ child_pane_id INTEGER, -- Nullable
+ child_group_id INTEGER, -- Nullable
+ index INTEGER,
+ PRIMARY KEY (workspace_id, group_id)
+) STRICT;
+
+CREATE TABLE pane_items(
+ workspace_id INTEGER,
+ pane_id INTEGER,
+ item_id INTEGER, -- Array
+ index INTEGER,
+ KEY (workspace_id, pane_id)
+) STRICT;
+";
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct PaneId {
+ workspace_id: WorkspaceId,
+ pane_id: usize,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct PaneGroupId {
+ workspace_id: WorkspaceId,
+ group_id: usize,
+}
+
+impl PaneGroupId {
+ pub(crate) fn root(workspace_id: WorkspaceId) -> Self {
+ Self {
+ workspace_id,
+ group_id: 0,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct SerializedPaneGroup {
+ group_id: PaneGroupId,
+ axis: Axis,
+ children: Vec<PaneGroupChild>,
+}
+
+struct PaneGroupChildRow {
+ child_pane_id: Option<usize>,
+ child_group_id: Option<usize>,
+ index: usize,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum PaneGroupChild {
+ Pane(SerializedPane),
+ Group(SerializedPaneGroup),
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct SerializedPane {
+ pane_id: PaneId,
+ children: Vec<ItemId>,
+}
+
+impl Db {
+ pub(crate) fn get_pane_group(&self, pane_group_id: PaneGroupId) -> SerializedPaneGroup {
+ let axis = self.get_pane_group_axis(pane_group_id);
+ let mut children: Vec<(usize, PaneGroupChild)> = Vec::new();
+ for child_row in self.get_pane_group_children(pane_group_id) {
+ if let Some(child_pane_id) = child_row.child_pane_id {
+ children.push((
+ child_row.index,
+ PaneGroupChild::Pane(self.get_pane(PaneId {
+ workspace_id: pane_group_id.workspace_id,
+ pane_id: child_pane_id,
+ })),
+ ));
+ } else if let Some(child_group_id) = child_row.child_group_id {
+ children.push((
+ child_row.index,
+ PaneGroupChild::Group(self.get_pane_group(PaneGroupId {
+ workspace_id: pane_group_id.workspace_id,
+ group_id: child_group_id,
+ })),
+ ));
+ }
+ }
+ children.sort_by_key(|(index, _)| index);
+
+ SerializedPaneGroup {
+ group_id: pane_group_id,
+ axis,
+ children: children.into_iter().map(|(_, child)| child).collect(),
+ }
+ }
+
+ pub fn get_pane_group_children(
+ &self,
+ pane_group_id: PaneGroupId,
+ ) -> impl Iterator<Item = PaneGroupChildRow> {
+ unimplemented!()
+ }
+
+ pub fn get_pane_group_axis(&self, pane_group_id: PaneGroupId) -> Axis {
+ unimplemented!();
+ }
+
+ pub fn save_center_pane_group(&self, center_pane_group: SerializedPaneGroup) {
+ // Delete the center pane group for this workspace and any of its children
+ // Generate new pane group IDs as we go through
+ // insert them
+ // Items garbage collect themselves when dropped
+ }
+
+ pub(crate) fn get_pane(&self, pane_id: PaneId) -> SerializedPane {
+ unimplemented!();
+ }
+
+ pub fn save_pane(&self, pane: SerializedPane) {}
+}
@@ -1,5 +1,7 @@
use std::{path::Path, sync::Arc};
+use crate::pane::{PaneGroupId, PaneId, SerializedPane, SerializedPaneGroup};
+
use super::Db;
pub(crate) const WORKSPACE_M_1: &str = "
@@ -17,28 +19,6 @@ CREATE TABLE worktree_roots(
workspace_id INTEGER NOT NULL,
FOREIGN KEY(workspace_id) REFERENCES workspace_ids(workspace_id)
) STRICT;
-
-CREATE TABLE pane_groups(
- workspace_id INTEGER,
- group_id INTEGER,
- split_direction STRING, -- 'Vertical' / 'Horizontal' /
- PRIMARY KEY (workspace_id, group_id)
-) STRICT;
-
-CREATE TABLE pane_group_children(
- workspace_id INTEGER,
- group_id INTEGER,
- child_pane_id INTEGER, -- Nullable
- child_group_id INTEGER, -- Nullable
- PRIMARY KEY (workspace_id, group_id)
-) STRICT;
-
-CREATE TABLE pane_items(
- workspace_id INTEGER,
- pane_id INTEGER,
- item_id INTEGER, -- Array
- PRIMARY KEY (workspace_id, pane_id)
-) STRICT;
";
// Zed stores items with ids which are a combination of a view id during a given run and a workspace id. This
@@ -52,18 +32,65 @@ CREATE TABLE pane_items(
// Case 4: Starting Zed with multiple project folders
// > Zed ~/projects/Zed ~/projects/Zed.dev
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct WorkspaceId(usize);
+struct WorkspaceRow {
+ pub workspace_id: WorkspaceId,
+ pub center_group_id: PaneGroupId,
+ pub dock_pane_id: PaneId,
+}
+
+pub struct SerializedWorkspace {
+ pub workspace_id: WorkspaceId,
+ pub center_group: SerializedPaneGroup,
+ pub dock_pane: Option<SerializedPane>,
+}
+
impl Db {
/// Finds or creates a workspace id for the given set of worktree roots. If the passed worktree roots is empty, return the
/// the last workspace id
- pub fn workspace_id(&self, worktree_roots: &[Arc<Path>]) -> WorkspaceId {
+ pub fn workspace_for_worktree_roots(
+ &self,
+ worktree_roots: &[Arc<Path>],
+ ) -> SerializedWorkspace {
// Find the workspace id which is uniquely identified by this set of paths return it if found
- // Otherwise:
- // Find the max workspace_id and increment it as our new workspace id
- // Store in the worktrees table the mapping from this new id to the set of worktree roots
- unimplemented!();
+ if let Some(workspace_id) = self.workspace_id(worktree_roots) {
+ let workspace_row = self.get_workspace_row(workspace_id);
+ let center_group = self.get_pane_group(workspace_row.center_group_id);
+ let dock_pane = self.get_pane(workspace_row.dock_pane_id);
+
+ SerializedWorkspace {
+ workspace_id,
+ center_group,
+ dock_pane: Some(dock_pane),
+ }
+ } else {
+ let workspace_id = self.get_next_workspace_id();
+ let center_group = SerializedPaneGroup {
+ group_id: PaneGroupId::root(workspace_id),
+ axis: Default::default(),
+ children: Default::default(),
+ };
+
+ SerializedWorkspace {
+ workspace_id,
+ center_group,
+ dock_pane: None,
+ }
+ }
+ }
+
+ fn get_next_workspace_id(&self) -> WorkspaceId {
+ unimplemented!()
+ }
+
+ fn workspace_id(&self, worktree_roots: &[Arc<Path>]) -> Option<WorkspaceId> {
+ unimplemented!()
+ }
+
+ fn get_workspace_row(&self, workspace_id: WorkspaceId) -> WorkspaceRow {
+ unimplemented!()
}
/// Updates the open paths for the given workspace id. Will garbage collect items from
@@ -80,16 +107,12 @@ impl Db {
unimplemented!();
}
- /// Returns the previous workspace ids sorted by last modified
+ /// Returns the previous workspace ids sorted by last modified along with their opened worktree roots
pub fn recent_workspaces(&self) -> Vec<(WorkspaceId, Vec<Arc<Path>>)> {
// Return all the workspace ids and their associated paths ordered by the access timestamp
//ORDER BY timestamps
unimplemented!();
}
-
- pub fn center_pane(&self, workspace: WorkspaceId) -> SerializedPaneGroup {}
-
- pub fn dock_pane(&self, workspace: WorkspaceId) -> SerializedPane {}
}
#[cfg(test)]
@@ -104,6 +127,42 @@ mod tests {
use super::WorkspaceId;
+ fn arc_path(path: &'static str) -> Arc<Path> {
+ PathBuf::from(path).into()
+ }
+
+ fn test_detect_workspace_id() {
+ let data = &[
+ (WorkspaceId(1), vec![arc_path("/tmp")]),
+ (WorkspaceId(2), vec![arc_path("/tmp"), arc_path("/tmp2")]),
+ (
+ WorkspaceId(3),
+ vec![arc_path("/tmp"), arc_path("/tmp2"), arc_path("/tmp3")],
+ ),
+ ];
+
+ let db = Db::open_in_memory();
+
+ for (workspace_id, entries) in data {
+ db.update_worktree_roots(workspace_id, entries); //??
+ }
+
+ assert_eq!(None, db.workspace_id(&[arc_path("/tmp2")]));
+ assert_eq!(
+ None,
+ db.workspace_id(&[arc_path("/tmp2"), arc_path("/tmp3")])
+ );
+ assert_eq!(Some(WorkspaceId(1)), db.workspace_id(&[arc_path("/tmp")]));
+ assert_eq!(
+ Some(WorkspaceId(2)),
+ db.workspace_id(&[arc_path("/tmp"), arc_path("/tmp2")])
+ );
+ assert_eq!(
+ Some(WorkspaceId(3)),
+ db.workspace_id(&[arc_path("/tmp"), arc_path("/tmp2"), arc_path("/tmp3")])
+ );
+ }
+
fn test_tricky_overlapping_updates() {
// DB state:
// (/tree) -> ID: 1
@@ -117,10 +176,6 @@ mod tests {
// (/tree2, /tree3) -> ID: 2
// Get rid of 3 for garbage collection
- fn arc_path(path: &'static str) -> Arc<Path> {
- PathBuf::from(path).into()
- }
-
let data = &[
(WorkspaceId(1), vec![arc_path("/tmp")]),
(WorkspaceId(2), vec![arc_path("/tmp"), arc_path("/tmp2")]),
@@ -131,18 +186,18 @@ mod tests {
for (workspace_id, entries) in data {
db.update_worktree_roots(workspace_id, entries); //??
- assert_eq!(&db.workspace_id(&[]), workspace_id)
+ assert_eq!(&db.workspace_id(&[]), &Some(*workspace_id))
}
for (workspace_id, entries) in data {
- assert_eq!(&db.workspace_id(entries.as_slice()), workspace_id);
+ assert_eq!(&db.workspace_id(entries.as_slice()), &Some(*workspace_id));
}
db.update_worktree_roots(&WorkspaceId(2), &[arc_path("/tmp2")]);
// todo!(); // make sure that 3 got garbage collected
- assert_eq!(db.workspace_id(&[arc_path("/tmp2")]), WorkspaceId(2));
- assert_eq!(db.workspace_id(&[arc_path("/tmp")]), WorkspaceId(1));
+ assert_eq!(db.workspace_id(&[arc_path("/tmp2")]), Some(WorkspaceId(2)));
+ assert_eq!(db.workspace_id(&[arc_path("/tmp")]), Some(WorkspaceId(1)));
let recent_workspaces = db.recent_workspaces();
assert_eq!(recent_workspaces.get(0).unwrap().0, WorkspaceId(2));
@@ -863,8 +863,9 @@ pub struct DebugContext<'a> {
pub app: &'a AppContext,
}
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum Axis {
+ #[default]
Horizontal,
Vertical,
}
@@ -18,6 +18,7 @@ test-support = [
]
[dependencies]
+db = { path = "../db" }
call = { path = "../call" }
client = { path = "../client" }
collections = { path = "../collections" }
@@ -137,7 +137,11 @@ pub struct Dock {
}
impl Dock {
- pub fn new(cx: &mut ViewContext<Workspace>, default_item_factory: DefaultItemFactory) -> Self {
+ pub fn new(
+ serialized_pane: SerializedPane,
+ default_item_factory: DefaultItemFactory,
+ cx: &mut ViewContext<Workspace>,
+ ) -> Self {
let anchor = cx.global::<Settings>().default_dock_anchor;
let pane = cx.add_view(|cx| Pane::new(Some(anchor), cx));
pane.update(cx, |pane, cx| {
@@ -1110,6 +1110,7 @@ enum FollowerItem {
impl Workspace {
pub fn new(
+ serialized_workspace: SerializedWorkspace,
project: ModelHandle<Project>,
dock_default_factory: DefaultItemFactory,
cx: &mut ViewContext<Self>,