diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 79695004d29aa6ebc496b708f12cc9fcf9bbec94..df62580c4d0a66bffc3899aa3c4a0f571c0fda53 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -2377,11 +2377,20 @@ impl MutableAppContext { let window = this.cx.windows.get_mut(&window_id)?; window.is_fullscreen = is_fullscreen; - let mut observations = this.window_fullscreen_observations.clone(); - observations.emit(window_id, this, |callback, this| { + let mut fullscreen_observations = this.window_fullscreen_observations.clone(); + fullscreen_observations.emit(window_id, this, |callback, this| { callback(is_fullscreen, this) }); + let bounds = if this.window_is_fullscreen(window_id) { + WindowBounds::Fullscreen + } else { + WindowBounds::Fixed(this.window_bounds(window_id)) + }; + + let mut bounds_observations = this.window_bounds_observations.clone(); + bounds_observations.emit(window_id, this, |callback, this| callback(bounds, this)); + Some(()) }); } diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 9d2cd1336d88237d6746fe0adcf29eca4cf56b77..66c991f0a87f94916f21208a2f9b013da4d2b074 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -23,7 +23,7 @@ use pathfinder_geometry::vector::{vec2f, Vector2F}; use serde_json::json; use smallvec::SmallVec; use sqlez::{ - bindable::{Bind, Column, StaticRowComponent}, + bindable::{Bind, Column, StaticColumnCount}, statement::Statement, }; use std::{ @@ -932,7 +932,7 @@ impl ToJson for Axis { } } -impl StaticRowComponent for Axis {} +impl StaticColumnCount for Axis {} impl Bind for Axis { fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result { match self { diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 92236bad616f9e54499b23d0c16d290640035717..6e25222d96eb83079db7c1805dc7d32b8aa198e3 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -15,7 +15,7 @@ use schemars::{ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json::Value; use sqlez::{ - bindable::{Bind, Column, StaticRowComponent}, + bindable::{Bind, Column, StaticColumnCount}, statement::Statement, }; use std::{collections::HashMap, fmt::Write as _, num::NonZeroU32, str, sync::Arc}; @@ -253,7 +253,7 @@ pub enum DockAnchor { Expanded, } -impl StaticRowComponent for DockAnchor {} +impl StaticColumnCount for DockAnchor {} impl Bind for DockAnchor { fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result { match self { diff --git a/crates/sqlez/src/bindable.rs b/crates/sqlez/src/bindable.rs index 51d4f5d4a365fd669508f66e9d436ce16aab40c4..9d5d0c8b2f9a62a43dddca9b8c9460bb0edb5f9f 100644 --- a/crates/sqlez/src/bindable.rs +++ b/crates/sqlez/src/bindable.rs @@ -9,37 +9,21 @@ use anyhow::{Context, Result}; use crate::statement::{SqlType, Statement}; -pub trait StaticRowComponent { - fn static_column_count() -> usize { +pub trait StaticColumnCount { + fn column_count() -> usize { 1 } } -pub trait RowComponent { - fn column_count(&self) -> usize; -} - -impl RowComponent for T { - fn column_count(&self) -> usize { - T::static_column_count() - } -} - -impl StaticRowComponent for &T { - fn static_column_count() -> usize { - T::static_column_count() - } -} - -pub trait Bind: RowComponent { +pub trait Bind { fn bind(&self, statement: &Statement, start_index: i32) -> Result; } -pub trait Column: Sized + RowComponent { +pub trait Column: Sized { fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)>; } -impl StaticRowComponent for bool {} +impl StaticColumnCount for bool {} impl Bind for bool { fn bind(&self, statement: &Statement, start_index: i32) -> Result { statement @@ -56,7 +40,7 @@ impl Column for bool { } } -impl StaticRowComponent for &[u8] {} +impl StaticColumnCount for &[u8] {} impl Bind for &[u8] { fn bind(&self, statement: &Statement, start_index: i32) -> Result { statement @@ -66,7 +50,7 @@ impl Bind for &[u8] { } } -impl StaticRowComponent for &[u8; C] {} +impl StaticColumnCount for &[u8; C] {} impl Bind for &[u8; C] { fn bind(&self, statement: &Statement, start_index: i32) -> Result { statement @@ -76,7 +60,7 @@ impl Bind for &[u8; C] { } } -impl StaticRowComponent for Vec {} +impl StaticColumnCount for Vec {} impl Bind for Vec { fn bind(&self, statement: &Statement, start_index: i32) -> Result { statement @@ -96,7 +80,7 @@ impl Column for Vec { } } -impl StaticRowComponent for f64 {} +impl StaticColumnCount for f64 {} impl Bind for f64 { fn bind(&self, statement: &Statement, start_index: i32) -> Result { statement @@ -116,7 +100,7 @@ impl Column for f64 { } } -impl StaticRowComponent for f32 {} +impl StaticColumnCount for f32 {} impl Bind for f32 { fn bind(&self, statement: &Statement, start_index: i32) -> Result { statement @@ -137,7 +121,7 @@ impl Column for f32 { } } -impl StaticRowComponent for i32 {} +impl StaticColumnCount for i32 {} impl Bind for i32 { fn bind(&self, statement: &Statement, start_index: i32) -> Result { statement @@ -155,7 +139,7 @@ impl Column for i32 { } } -impl StaticRowComponent for i64 {} +impl StaticColumnCount for i64 {} impl Bind for i64 { fn bind(&self, statement: &Statement, start_index: i32) -> Result { statement @@ -172,7 +156,7 @@ impl Column for i64 { } } -impl StaticRowComponent for u32 {} +impl StaticColumnCount for u32 {} impl Bind for u32 { fn bind(&self, statement: &Statement, start_index: i32) -> Result { (*self as i64) @@ -188,7 +172,7 @@ impl Column for u32 { } } -impl StaticRowComponent for usize {} +impl StaticColumnCount for usize {} impl Bind for usize { fn bind(&self, statement: &Statement, start_index: i32) -> Result { (*self as i64) @@ -204,7 +188,7 @@ impl Column for usize { } } -impl StaticRowComponent for &str {} +impl StaticColumnCount for &str {} impl Bind for &str { fn bind(&self, statement: &Statement, start_index: i32) -> Result { statement.bind_text(start_index, self)?; @@ -212,7 +196,7 @@ impl Bind for &str { } } -impl StaticRowComponent for Arc {} +impl StaticColumnCount for Arc {} impl Bind for Arc { fn bind(&self, statement: &Statement, start_index: i32) -> Result { statement.bind_text(start_index, self.as_ref())?; @@ -220,7 +204,7 @@ impl Bind for Arc { } } -impl StaticRowComponent for String {} +impl StaticColumnCount for String {} impl Bind for String { fn bind(&self, statement: &Statement, start_index: i32) -> Result { statement.bind_text(start_index, self)?; @@ -242,17 +226,18 @@ impl Column for String { } } -impl StaticRowComponent for Option { - fn static_column_count() -> usize { - T::static_column_count() +impl StaticColumnCount for Option { + fn column_count() -> usize { + T::column_count() } } -impl Bind for Option { +impl Bind for Option { fn bind(&self, statement: &Statement, mut start_index: i32) -> Result { if let Some(this) = self { this.bind(statement, start_index) } else { - for _ in 0..T::static_column_count() { + for i in 0..T::column_count() { + dbg!(i); statement.bind_null(start_index)?; start_index += 1; } @@ -261,22 +246,22 @@ impl Bind for Option { } } -impl Column for Option { +impl Column for Option { fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> { if let SqlType::Null = statement.column_type(start_index)? { - Ok((None, start_index + T::static_column_count() as i32)) + Ok((None, start_index + T::column_count() as i32)) } else { T::column(statement, start_index).map(|(result, next_index)| (Some(result), next_index)) } } } -impl StaticRowComponent for [T; COUNT] { - fn static_column_count() -> usize { - T::static_column_count() * COUNT +impl StaticColumnCount for [T; COUNT] { + fn column_count() -> usize { + T::column_count() * COUNT } } -impl Bind for [T; COUNT] { +impl Bind for [T; COUNT] { fn bind(&self, statement: &Statement, start_index: i32) -> Result { let mut current_index = start_index; for binding in self { @@ -287,7 +272,7 @@ impl Bind for [T; COUNT] { } } -impl Column for [T; COUNT] { +impl Column for [T; COUNT] { fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> { let mut array = [Default::default(); COUNT]; let mut current_index = start_index; @@ -298,21 +283,21 @@ impl Column } } -impl StaticRowComponent for &Path {} +impl StaticColumnCount for &Path {} impl Bind for &Path { fn bind(&self, statement: &Statement, start_index: i32) -> Result { self.as_os_str().as_bytes().bind(statement, start_index) } } -impl StaticRowComponent for Arc {} +impl StaticColumnCount for Arc {} impl Bind for Arc { fn bind(&self, statement: &Statement, start_index: i32) -> Result { self.as_ref().bind(statement, start_index) } } -impl StaticRowComponent for PathBuf {} +impl StaticColumnCount for PathBuf {} impl Bind for PathBuf { fn bind(&self, statement: &Statement, start_index: i32) -> Result { (self.as_ref() as &Path).bind(statement, start_index) @@ -330,8 +315,8 @@ impl Column for PathBuf { } } -impl StaticRowComponent for () { - fn static_column_count() -> usize { +impl StaticColumnCount for () { + fn column_count() -> usize { 0 } } @@ -350,11 +335,10 @@ impl Column for () { macro_rules! impl_tuple_row_traits { ( $($local:ident: $type:ident),+ ) => { - impl<$($type: RowComponent),+> RowComponent for ($($type,)+) { - fn column_count(&self) -> usize { - let ($($local,)+) = self; + impl<$($type: StaticColumnCount),+> StaticColumnCount for ($($type,)+) { + fn column_count() -> usize { let mut count = 0; - $(count += $local.column_count();)+ + $(count += $type::column_count();)+ count } } diff --git a/crates/sqlez/src/statement.rs b/crates/sqlez/src/statement.rs index da4f2388d9f08baa9186dcf4239d87eda42d0503..69d5685ba02ceadeeb5bec364366c76121aa12a9 100644 --- a/crates/sqlez/src/statement.rs +++ b/crates/sqlez/src/statement.rs @@ -238,15 +238,11 @@ impl<'a> Statement<'a> { pub fn bind(&self, value: T, index: i32) -> Result { debug_assert!(index > 0); - let after = value.bind(self, index)?; - debug_assert_eq!((after - index) as usize, value.column_count()); - Ok(after) + Ok(value.bind(self, index)?) } pub fn column(&mut self) -> Result { - let (result, after) = T::column(self, 0)?; - debug_assert_eq!(T::column_count(&result), after as usize); - Ok(result) + Ok(T::column(self, 0)?.0) } pub fn column_type(&mut self, index: i32) -> Result { @@ -263,9 +259,7 @@ impl<'a> Statement<'a> { } pub fn with_bindings(&mut self, bindings: impl Bind) -> Result<&mut Self> { - let column_count = bindings.column_count(); - let after = self.bind(bindings, 1)?; - debug_assert_eq!((after - 1) as usize, column_count); + self.bind(bindings, 1)?; Ok(self) } diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index 1e8e72a3d228c684523a93e34bf2daf0d7d771a0..7711ee5141319cf88f44b721bce3c79874a327d1 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -65,70 +65,70 @@ define_connection! { // active: bool, // Indicates if this item is the active one in the pane // ) pub static ref DB: WorkspaceDb<()> = - &[sql!( - CREATE TABLE workspaces( - workspace_id INTEGER PRIMARY KEY, - workspace_location BLOB UNIQUE, - 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 - left_sidebar_open INTEGER, //Boolean - timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL, - FOREIGN KEY(dock_pane) REFERENCES panes(pane_id) - ) STRICT; - - CREATE TABLE pane_groups( - group_id INTEGER PRIMARY KEY, - workspace_id INTEGER NOT NULL, - parent_group_id INTEGER, // NULL indicates that this is a root node - position 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 - ON UPDATE CASCADE, - FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE - ) STRICT; - - CREATE TABLE panes( - pane_id INTEGER PRIMARY KEY, - workspace_id INTEGER NOT NULL, - active INTEGER NOT NULL, // Boolean - FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) - ON DELETE CASCADE - ON UPDATE CASCADE - ) STRICT; - - CREATE TABLE center_panes( - pane_id INTEGER PRIMARY KEY, - parent_group_id INTEGER, // NULL means that this is a root pane - position INTEGER, // NULL means that this is a root pane - FOREIGN KEY(pane_id) REFERENCES panes(pane_id) - ON DELETE CASCADE, - FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE - ) STRICT; - - CREATE TABLE items( - item_id INTEGER NOT NULL, // This is the item's view id, so this is not unique - workspace_id INTEGER NOT NULL, - pane_id INTEGER NOT NULL, - kind TEXT NOT NULL, - position INTEGER NOT NULL, - active INTEGER NOT NULL, - FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) - ON DELETE CASCADE - ON UPDATE CASCADE, - FOREIGN KEY(pane_id) REFERENCES panes(pane_id) - ON DELETE CASCADE, - PRIMARY KEY(item_id, workspace_id) - ) STRICT; - ), - sql!( - ALTER TABLE workspaces ADD COLUMN fullscreen INTEGER; // Boolean - ALTER TABLE workspaces ADD COLUMN window_x REAL; // Null means set to whatever - ALTER TABLE workspaces ADD COLUMN window_y REAL; // Null means set to whatever - ALTER TABLE workspaces ADD COLUMN window_width REAL; // Null means set to whatever - ALTER TABLE workspaces ADD COLUMN window_height REAL; // Null means set to whatever - )]; + &[sql!( + CREATE TABLE workspaces( + workspace_id INTEGER PRIMARY KEY, + workspace_location BLOB UNIQUE, + 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 + left_sidebar_open INTEGER, //Boolean + timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL, + FOREIGN KEY(dock_pane) REFERENCES panes(pane_id) + ) STRICT; + + CREATE TABLE pane_groups( + group_id INTEGER PRIMARY KEY, + workspace_id INTEGER NOT NULL, + parent_group_id INTEGER, // NULL indicates that this is a root node + position 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 + ON UPDATE CASCADE, + FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE + ) STRICT; + + CREATE TABLE panes( + pane_id INTEGER PRIMARY KEY, + workspace_id INTEGER NOT NULL, + active INTEGER NOT NULL, // Boolean + FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) + ON DELETE CASCADE + ON UPDATE CASCADE + ) STRICT; + + CREATE TABLE center_panes( + pane_id INTEGER PRIMARY KEY, + parent_group_id INTEGER, // NULL means that this is a root pane + position INTEGER, // NULL means that this is a root pane + FOREIGN KEY(pane_id) REFERENCES panes(pane_id) + ON DELETE CASCADE, + FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE + ) STRICT; + + CREATE TABLE items( + item_id INTEGER NOT NULL, // This is the item's view id, so this is not unique + workspace_id INTEGER NOT NULL, + pane_id INTEGER NOT NULL, + kind TEXT NOT NULL, + position INTEGER NOT NULL, + active INTEGER NOT NULL, + FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) + ON DELETE CASCADE + ON UPDATE CASCADE, + FOREIGN KEY(pane_id) REFERENCES panes(pane_id) + ON DELETE CASCADE, + PRIMARY KEY(item_id, workspace_id) + ) STRICT; + ), + sql!( + ALTER TABLE workspaces ADD COLUMN fullscreen INTEGER; // Boolean + ALTER TABLE workspaces ADD COLUMN window_x REAL; // Null means set to whatever + ALTER TABLE workspaces ADD COLUMN window_y REAL; // Null means set to whatever + ALTER TABLE workspaces ADD COLUMN window_width REAL; // Null means set to whatever + ALTER TABLE workspaces ADD COLUMN window_height REAL; // Null means set to whatever + )]; } impl WorkspaceDb { @@ -140,16 +140,16 @@ impl WorkspaceDb { worktree_roots: &[P], ) -> Option { let workspace_location: WorkspaceLocation = worktree_roots.into(); - + // Note that we re-assign the workspace_id here in case it's empty // and we've grabbed the most recent workspace - let (workspace_id, workspace_location, left_sidebar_open, dock_position, fullscreen, window_x, window_y, window_width, window_height): ( + let (workspace_id, workspace_location, left_sidebar_open, dock_position, fullscreen, window_region): ( WorkspaceId, WorkspaceLocation, bool, DockPosition, bool, - f32, f32, f32, f32 + Option<(f32, f32, f32, f32)> ) = self .select_row_bound(sql! { SELECT @@ -170,7 +170,7 @@ impl WorkspaceDb { .context("No workspaces found") .warn_on_err() .flatten()?; - + Some(SerializedWorkspace { id: workspace_id, location: workspace_location.clone(), @@ -185,13 +185,13 @@ impl WorkspaceDb { dock_position, left_sidebar_open, fullscreen, - bounds: Some(RectF::new( - Vector2F::new(window_x, window_y), - Vector2F::new(window_width, window_height) + bounds: dbg!(window_region).map(|(x, y, width, height)| RectF::new( + Vector2F::new(x, y), + Vector2F::new(width, height) )) }) } - + /// Saves a workspace using the worktree roots. Will garbage collect any workspaces /// that used this workspace previously pub async fn save_workspace(&self, workspace: SerializedWorkspace) { @@ -203,30 +203,30 @@ impl WorkspaceDb { DELETE FROM pane_groups WHERE workspace_id = ?1; DELETE FROM panes WHERE workspace_id = ?1;))?(workspace.id) .expect("Clearing old panes"); - + conn.exec_bound(sql!( DELETE FROM workspaces WHERE workspace_location = ? AND workspace_id != ? ))?((&workspace.location, workspace.id.clone())) .context("clearing out old locations")?; - + // Upsert conn.exec_bound(sql!( - INSERT INTO workspaces( - workspace_id, - workspace_location, - left_sidebar_open, - dock_visible, - dock_anchor, - timestamp - ) - VALUES (?1, ?2, ?3, ?4, ?5, CURRENT_TIMESTAMP) - ON CONFLICT DO - UPDATE SET - workspace_location = ?2, - left_sidebar_open = ?3, - dock_visible = ?4, - dock_anchor = ?5, - timestamp = CURRENT_TIMESTAMP + INSERT INTO workspaces( + workspace_id, + workspace_location, + left_sidebar_open, + dock_visible, + dock_anchor, + timestamp + ) + VALUES (?1, ?2, ?3, ?4, ?5, CURRENT_TIMESTAMP) + ON CONFLICT DO + UPDATE SET + workspace_location = ?2, + left_sidebar_open = ?3, + dock_visible = ?4, + dock_anchor = ?5, + timestamp = CURRENT_TIMESTAMP ))?(( workspace.id, &workspace.location, @@ -234,35 +234,35 @@ impl WorkspaceDb { workspace.dock_position, )) .context("Updating workspace")?; - + // Save center pane group and dock pane Self::save_pane_group(conn, workspace.id, &workspace.center_group, None) .context("save pane group in save workspace")?; - + let dock_id = Self::save_pane(conn, workspace.id, &workspace.dock_pane, None, true) .context("save pane in save workspace")?; - + // Complete workspace initialization conn.exec_bound(sql!( UPDATE workspaces SET dock_pane = ? - WHERE workspace_id = ? + WHERE workspace_id = ? ))?((dock_id, workspace.id)) .context("Finishing initialization with dock pane")?; - + Ok(()) }) .log_err(); }) .await; } - + query! { pub async fn next_id() -> Result { INSERT INTO workspaces DEFAULT VALUES RETURNING workspace_id } } - + query! { fn recent_workspaces() -> Result> { SELECT workspace_id, workspace_location @@ -271,14 +271,14 @@ impl WorkspaceDb { ORDER BY timestamp DESC } } - + query! { async fn delete_stale_workspace(id: WorkspaceId) -> Result<()> { DELETE FROM workspaces WHERE workspace_id IS ? } } - + // Returns the recent locations which are still valid on disk and deletes ones which no longer // exist. pub async fn recent_workspaces_on_disk(&self) -> Result> { @@ -286,18 +286,18 @@ impl WorkspaceDb { let mut delete_tasks = Vec::new(); for (id, location) in self.recent_workspaces()? { if location.paths().iter().all(|path| path.exists()) - && location.paths().iter().any(|path| path.is_dir()) + && location.paths().iter().any(|path| path.is_dir()) { result.push((id, location)); } else { delete_tasks.push(self.delete_stale_workspace(id)); } } - + futures::future::join_all(delete_tasks).await; Ok(result) } - + pub async fn last_workspace(&self) -> Result> { Ok(self .recent_workspaces_on_disk() @@ -306,7 +306,7 @@ impl WorkspaceDb { .next() .map(|(_, location)| location)) } - + fn get_center_pane_group(&self, workspace_id: WorkspaceId) -> Result { Ok(self .get_pane_group(workspace_id, None)? @@ -319,7 +319,7 @@ impl WorkspaceDb { }) })) } - + fn get_pane_group( &self, workspace_id: WorkspaceId, @@ -330,27 +330,27 @@ impl WorkspaceDb { self.select_bound::(sql!( SELECT group_id, axis, pane_id, active FROM (SELECT - group_id, - axis, - NULL as pane_id, - NULL as active, - position, - parent_group_id, - workspace_id - FROM pane_groups - UNION - SELECT - NULL, - NULL, - center_panes.pane_id, - panes.active as active, - position, - parent_group_id, - panes.workspace_id as workspace_id - FROM center_panes - JOIN panes ON center_panes.pane_id = panes.pane_id) - WHERE parent_group_id IS ? AND workspace_id = ? - ORDER BY position + group_id, + axis, + NULL as pane_id, + NULL as active, + position, + parent_group_id, + workspace_id + FROM pane_groups + UNION + SELECT + NULL, + NULL, + center_panes.pane_id, + panes.active as active, + position, + parent_group_id, + panes.workspace_id as workspace_id + FROM center_panes + JOIN panes ON center_panes.pane_id = panes.pane_id) + WHERE parent_group_id IS ? AND workspace_id = ? + ORDER BY position ))?((group_id, workspace_id))? .into_iter() .map(|(group_id, axis, pane_id, active)| { @@ -376,7 +376,7 @@ impl WorkspaceDb { }) .collect::>() } - + fn save_pane_group( conn: &Connection, workspace_id: WorkspaceId, @@ -386,18 +386,18 @@ impl WorkspaceDb { match pane_group { SerializedPaneGroup::Group { axis, children } => { let (parent_id, position) = unzip_option(parent); - + let group_id = conn.select_row_bound::<_, i64>(sql!( - INSERT INTO pane_groups(workspace_id, parent_group_id, position, axis) - VALUES (?, ?, ?, ?) - RETURNING group_id + INSERT INTO pane_groups(workspace_id, parent_group_id, position, axis) + VALUES (?, ?, ?, ?) + RETURNING group_id ))?((workspace_id, parent_id, position, *axis))? .ok_or_else(|| anyhow!("Couldn't retrieve group_id from inserted pane_group"))?; - + for (position, group) in children.iter().enumerate() { Self::save_pane_group(conn, workspace_id, group, Some((group_id, position)))? } - + Ok(()) } SerializedPaneGroup::Pane(pane) => { @@ -406,7 +406,7 @@ impl WorkspaceDb { } } } - + fn get_dock_pane(&self, workspace_id: WorkspaceId) -> Result { let (pane_id, active) = self.select_row_bound(sql!( SELECT pane_id, active @@ -414,13 +414,13 @@ impl WorkspaceDb { WHERE pane_id = (SELECT dock_pane FROM workspaces WHERE workspace_id = ?) ))?(workspace_id)? .context("No dock pane for workspace")?; - + Ok(SerializedPane::new( self.get_items(pane_id).context("Reading items")?, active, )) } - + fn save_pane( conn: &Connection, workspace_id: WorkspaceId, @@ -434,7 +434,7 @@ impl WorkspaceDb { RETURNING pane_id ))?((workspace_id, pane.active))? .ok_or_else(|| anyhow!("Could not retrieve inserted pane_id"))?; - + if !dock { let (parent_id, order) = unzip_option(parent); conn.exec_bound(sql!( @@ -442,20 +442,20 @@ impl WorkspaceDb { VALUES (?, ?, ?) ))?((pane_id, parent_id, order))?; } - + Self::save_items(conn, workspace_id, pane_id, &pane.children).context("Saving items")?; - + Ok(pane_id) } - + fn get_items(&self, pane_id: PaneId) -> Result> { Ok(self.select_bound(sql!( SELECT kind, item_id, active FROM items WHERE pane_id = ? - ORDER BY position + ORDER BY position ))?(pane_id)?) } - + fn save_items( conn: &Connection, workspace_id: WorkspaceId, @@ -468,10 +468,10 @@ impl WorkspaceDb { for (position, item) in items.iter().enumerate() { insert((workspace_id, pane_id, position, item))?; } - + Ok(()) } - + query! { pub async fn update_timestamp(workspace_id: WorkspaceId) -> Result<()> { UPDATE workspaces @@ -479,9 +479,9 @@ impl WorkspaceDb { WHERE workspace_id = ? } } - + query! { - pub async fn set_bounds(workspace_id: WorkspaceId, fullscreen: bool, x: f32, y: f32, width: f32, height: f32) -> Result<()> { + pub async fn set_bounds(workspace_id: WorkspaceId, fullscreen: bool, bounds: Option<(f32, f32, f32, f32)>) -> Result<()> { UPDATE workspaces SET fullscreen = ?2, window_x = ?3, @@ -495,20 +495,20 @@ impl WorkspaceDb { #[cfg(test)] mod tests { - + use std::sync::Arc; - + use db::open_test_db; use settings::DockAnchor; - + use super::*; - + #[gpui::test] async fn test_next_id_stability() { env_logger::try_init().ok(); - + let db = WorkspaceDb(open_test_db("test_next_id_stability").await); - + db.write(|conn| { conn.migrate( "test_table", @@ -517,14 +517,14 @@ mod tests { text TEXT, workspace_id INTEGER, FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) - ON DELETE CASCADE + ON DELETE CASCADE ) STRICT; )], ) .unwrap(); }) .await; - + let id = db.next_id().await.unwrap(); // Assert the empty row got inserted assert_eq!( @@ -535,28 +535,28 @@ mod tests { .unwrap()(id) .unwrap() ); - + db.write(move |conn| { conn.exec_bound(sql!(INSERT INTO test_table(text, workspace_id) VALUES (?, ?))) .unwrap()(("test-text-1", id)) - .unwrap() + .unwrap() }) .await; - + let test_text_1 = db .select_row_bound::<_, String>(sql!(SELECT text FROM test_table WHERE workspace_id = ?)) .unwrap()(1) - .unwrap() - .unwrap(); + .unwrap() + .unwrap(); assert_eq!(test_text_1, "test-text-1"); } - + #[gpui::test] async fn test_workspace_id_stability() { env_logger::try_init().ok(); - + let db = WorkspaceDb(open_test_db("test_workspace_id_stability").await); - + db.write(|conn| { conn.migrate( "test_table", @@ -566,13 +566,13 @@ mod tests { workspace_id INTEGER, FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) - ON DELETE CASCADE + ON DELETE CASCADE ) STRICT;)], ) }) .await .unwrap(); - + let mut workspace_1 = SerializedWorkspace { id: 1, location: (["/tmp", "/tmp2"]).into(), @@ -583,7 +583,7 @@ mod tests { fullscreen: false, bounds: Default::default(), }; - + let mut workspace_2 = SerializedWorkspace { id: 2, location: (["/tmp"]).into(), @@ -594,57 +594,57 @@ mod tests { fullscreen: false, bounds: Default::default(), }; - + db.save_workspace(workspace_1.clone()).await; - + db.write(|conn| { conn.exec_bound(sql!(INSERT INTO test_table(text, workspace_id) VALUES (?, ?))) .unwrap()(("test-text-1", 1)) - .unwrap(); + .unwrap(); }) .await; - + db.save_workspace(workspace_2.clone()).await; - + db.write(|conn| { conn.exec_bound(sql!(INSERT INTO test_table(text, workspace_id) VALUES (?, ?))) .unwrap()(("test-text-2", 2)) - .unwrap(); + .unwrap(); }) .await; - + workspace_1.location = (["/tmp", "/tmp3"]).into(); db.save_workspace(workspace_1.clone()).await; db.save_workspace(workspace_1).await; - + workspace_2.dock_pane.children.push(SerializedItem { kind: Arc::from("Test"), item_id: 10, active: true, }); db.save_workspace(workspace_2).await; - + let test_text_2 = db .select_row_bound::<_, String>(sql!(SELECT text FROM test_table WHERE workspace_id = ?)) .unwrap()(2) - .unwrap() - .unwrap(); + .unwrap() + .unwrap(); assert_eq!(test_text_2, "test-text-2"); - + let test_text_1 = db .select_row_bound::<_, String>(sql!(SELECT text FROM test_table WHERE workspace_id = ?)) .unwrap()(1) - .unwrap() - .unwrap(); + .unwrap() + .unwrap(); assert_eq!(test_text_1, "test-text-1"); } - + #[gpui::test] async fn test_full_workspace_serialization() { env_logger::try_init().ok(); - + let db = WorkspaceDb(open_test_db("test_full_workspace_serialization").await); - + let dock_pane = crate::persistence::model::SerializedPane { children: vec![ SerializedItem::new("Terminal", 1, false), @@ -654,7 +654,7 @@ mod tests { ], active: false, }; - + // ----------------- // | 1,2 | 5,6 | // | - - - | | @@ -691,7 +691,7 @@ mod tests { )), ], }; - + let workspace = SerializedWorkspace { id: 5, location: (["/tmp", "/tmp2"]).into(), @@ -702,26 +702,26 @@ mod tests { fullscreen: false, bounds: Default::default(), }; - + db.save_workspace(workspace.clone()).await; let round_trip_workspace = db.workspace_for_roots(&["/tmp2", "/tmp"]); - + assert_eq!(workspace, round_trip_workspace.unwrap()); - + // Test guaranteed duplicate IDs db.save_workspace(workspace.clone()).await; db.save_workspace(workspace.clone()).await; - + let round_trip_workspace = db.workspace_for_roots(&["/tmp", "/tmp2"]); assert_eq!(workspace, round_trip_workspace.unwrap()); } - + #[gpui::test] async fn test_workspace_assignment() { env_logger::try_init().ok(); - + let db = WorkspaceDb(open_test_db("test_basic_functionality").await); - + let workspace_1 = SerializedWorkspace { id: 1, location: (["/tmp", "/tmp2"]).into(), @@ -732,7 +732,7 @@ mod tests { fullscreen: false, bounds: Default::default(), }; - + let mut workspace_2 = SerializedWorkspace { id: 2, location: (["/tmp"]).into(), @@ -743,10 +743,10 @@ mod tests { fullscreen: false, bounds: Default::default(), }; - + db.save_workspace(workspace_1.clone()).await; db.save_workspace(workspace_2.clone()).await; - + // Test that paths are treated as a set assert_eq!( db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(), @@ -756,20 +756,20 @@ mod tests { db.workspace_for_roots(&["/tmp2", "/tmp"]).unwrap(), workspace_1 ); - + // Make sure that other keys work assert_eq!(db.workspace_for_roots(&["/tmp"]).unwrap(), workspace_2); assert_eq!(db.workspace_for_roots(&["/tmp3", "/tmp2", "/tmp4"]), None); - + // Test 'mutate' case of updating a pre-existing id workspace_2.location = (["/tmp", "/tmp2"]).into(); - + db.save_workspace(workspace_2.clone()).await; assert_eq!( db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(), workspace_2 ); - + // Test other mechanism for mutating let mut workspace_3 = SerializedWorkspace { id: 3, @@ -781,13 +781,13 @@ mod tests { fullscreen: false, bounds: Default::default(), }; - + db.save_workspace(workspace_3.clone()).await; assert_eq!( db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(), workspace_3 ); - + // Make sure that updating paths differently also works workspace_3.location = (["/tmp3", "/tmp4", "/tmp2"]).into(); db.save_workspace(workspace_3.clone()).await; @@ -798,11 +798,11 @@ mod tests { workspace_3 ); } - + use crate::dock::DockPosition; use crate::persistence::model::SerializedWorkspace; use crate::persistence::model::{SerializedItem, SerializedPane, SerializedPaneGroup}; - + fn default_workspace>( workspace_id: &[P], dock_pane: SerializedPane, @@ -819,13 +819,13 @@ mod tests { bounds: Default::default(), } } - + #[gpui::test] async fn test_basic_dock_pane() { env_logger::try_init().ok(); - + let db = WorkspaceDb(open_test_db("basic_dock_pane").await); - + let dock_pane = crate::persistence::model::SerializedPane::new( vec![ SerializedItem::new("Terminal", 1, false), @@ -835,22 +835,22 @@ mod tests { ], false, ); - + let workspace = default_workspace(&["/tmp"], dock_pane, &Default::default()); - + db.save_workspace(workspace.clone()).await; - + let new_workspace = db.workspace_for_roots(&["/tmp"]).unwrap(); - + assert_eq!(workspace.dock_pane, new_workspace.dock_pane); } - + #[gpui::test] async fn test_simple_split() { env_logger::try_init().ok(); - + let db = WorkspaceDb(open_test_db("simple_split").await); - + // ----------------- // | 1,2 | 5,6 | // | - - - | | @@ -887,22 +887,22 @@ mod tests { )), ], }; - + let workspace = default_workspace(&["/tmp"], Default::default(), ¢er_pane); - + db.save_workspace(workspace.clone()).await; - + let new_workspace = db.workspace_for_roots(&["/tmp"]).unwrap(); - + assert_eq!(workspace.center_group, new_workspace.center_group); } - + #[gpui::test] async fn test_cleanup_panes() { env_logger::try_init().ok(); - + let db = WorkspaceDb(open_test_db("test_cleanup_panes").await); - + let center_pane = SerializedPaneGroup::Group { axis: gpui::Axis::Horizontal, children: vec![ @@ -934,13 +934,13 @@ mod tests { )), ], }; - + let id = &["/tmp"]; - + let mut workspace = default_workspace(id, Default::default(), ¢er_pane); - + db.save_workspace(workspace.clone()).await; - + workspace.center_group = SerializedPaneGroup::Group { axis: gpui::Axis::Vertical, children: vec![ @@ -960,11 +960,11 @@ mod tests { )), ], }; - + db.save_workspace(workspace.clone()).await; - + let new_workspace = db.workspace_for_roots(id).unwrap(); - + assert_eq!(workspace.center_group, new_workspace.center_group); } } diff --git a/crates/workspace/src/persistence/model.rs b/crates/workspace/src/persistence/model.rs index 4bc8881bbae6e1bf1067ab5163ca54872c03b6cc..f6236ced79f6e35e89baf3b384af01436394aebf 100644 --- a/crates/workspace/src/persistence/model.rs +++ b/crates/workspace/src/persistence/model.rs @@ -11,7 +11,7 @@ use gpui::{ }; use db::sqlez::{ - bindable::{Bind, Column, StaticRowComponent}, + bindable::{Bind, Column, StaticColumnCount}, statement::Statement, }; use project::Project; @@ -42,7 +42,7 @@ impl, T: IntoIterator> From for WorkspaceLocation { } } -impl StaticRowComponent for WorkspaceLocation {} +impl StaticColumnCount for WorkspaceLocation {} impl Bind for &WorkspaceLocation { fn bind(&self, statement: &Statement, start_index: i32) -> Result { bincode::serialize(&self.0) @@ -254,8 +254,8 @@ impl Default for SerializedItem { } } -impl StaticRowComponent for SerializedItem { - fn static_column_count() -> usize { +impl StaticColumnCount for SerializedItem { + fn column_count() -> usize { 3 } } @@ -283,8 +283,8 @@ impl Column for SerializedItem { } } -impl StaticRowComponent for DockPosition { - fn static_column_count() -> usize { +impl StaticColumnCount for DockPosition { + fn column_count() -> usize { 2 } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index addbb9b017f681b7ad51b77fa9fb682f366088f6..4c742cd7faf14f010e340f3e8aa0d8cacb99a03d 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -683,9 +683,9 @@ impl Workspace { // Use the serialized workspace to construct the new window let (_, workspace) = cx.add_window( - (app_state.build_window_options)( - serialized_workspace.as_ref().map(|sw| sw.bounds()), - ), + (app_state.build_window_options)(dbg!(serialized_workspace + .as_ref() + .map(|sw| sw.bounds()))), |cx| { let mut workspace = Workspace::new( serialized_workspace, @@ -697,15 +697,18 @@ impl Workspace { (app_state.initialize_workspace)(&mut workspace, &app_state, cx); cx.observe_window_bounds(move |_, bounds, cx| { let fullscreen = cx.window_is_fullscreen(cx.window_id()); - cx.background() - .spawn(DB.set_bounds( - workspace_id, - fullscreen, - bounds.min_x(), - bounds.min_y(), - bounds.width(), - bounds.height(), + let bounds = if let WindowBounds::Fixed(region) = bounds { + Some(( + region.min_x(), + region.min_y(), + region.width(), + region.height(), )) + } else { + None + }; + cx.background() + .spawn(DB.set_bounds(workspace_id, fullscreen, bounds)) .detach_and_log_err(cx); }) .detach();