Simplify the SerializableItem::cleanup implementation (#29567)
Release Notes: - N/A --------- Co-authored-by: Julia Ryan <juliaryan3.14@gmail.com>
This commit is contained in:
parent
17703310ae
commit
1d7c86bf0d
13 changed files with 120 additions and 200 deletions
|
@ -122,7 +122,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_saves_and_retrieves_command_invocation() {
|
async fn test_saves_and_retrieves_command_invocation() {
|
||||||
let db =
|
let db =
|
||||||
CommandPaletteDB(db::open_test_db("test_saves_and_retrieves_command_invocation").await);
|
CommandPaletteDB::open_test_db("test_saves_and_retrieves_command_invocation").await;
|
||||||
|
|
||||||
let retrieved_cmd = db.get_last_invoked("editor: backspace").unwrap();
|
let retrieved_cmd = db.get_last_invoked("editor: backspace").unwrap();
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_gets_usage_history() {
|
async fn test_gets_usage_history() {
|
||||||
let db = CommandPaletteDB(db::open_test_db("test_gets_usage_history").await);
|
let db = CommandPaletteDB::open_test_db("test_gets_usage_history").await;
|
||||||
db.write_command_invocation("go to line: toggle", "200")
|
db.write_command_invocation("go to line: toggle", "200")
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -167,7 +167,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_lists_ordered_by_usage() {
|
async fn test_lists_ordered_by_usage() {
|
||||||
let db = CommandPaletteDB(db::open_test_db("test_lists_ordered_by_usage").await);
|
let db = CommandPaletteDB::open_test_db("test_lists_ordered_by_usage").await;
|
||||||
|
|
||||||
let empty_commands = db.list_commands_used();
|
let empty_commands = db.list_commands_used();
|
||||||
match &empty_commands {
|
match &empty_commands {
|
||||||
|
@ -200,7 +200,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_handles_max_invocation_entries() {
|
async fn test_handles_max_invocation_entries() {
|
||||||
let db = CommandPaletteDB(db::open_test_db("test_handles_max_invocation_entries").await);
|
let db = CommandPaletteDB::open_test_db("test_handles_max_invocation_entries").await;
|
||||||
|
|
||||||
for i in 1..=1001 {
|
for i in 1..=1001 {
|
||||||
db.write_command_invocation("some-command", &i.to_string())
|
db.write_command_invocation("some-command", &i.to_string())
|
||||||
|
|
|
@ -23,7 +23,7 @@ use project::Project;
|
||||||
use ui::{Divider, HighlightedLabel, ListItem, ListSubHeader, prelude::*};
|
use ui::{Divider, HighlightedLabel, ListItem, ListSubHeader, prelude::*};
|
||||||
|
|
||||||
use ui_input::SingleLineInput;
|
use ui_input::SingleLineInput;
|
||||||
use workspace::{AppState, ItemId, SerializableItem};
|
use workspace::{AppState, ItemId, SerializableItem, delete_unloaded_items};
|
||||||
use workspace::{Item, Workspace, WorkspaceId, item::ItemEvent};
|
use workspace::{Item, Workspace, WorkspaceId, item::ItemEvent};
|
||||||
|
|
||||||
pub fn init(app_state: Arc<AppState>, cx: &mut App) {
|
pub fn init(app_state: Arc<AppState>, cx: &mut App) {
|
||||||
|
@ -860,11 +860,13 @@ impl SerializableItem for ComponentPreview {
|
||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Task<gpui::Result<()>> {
|
) -> Task<gpui::Result<()>> {
|
||||||
cx.background_spawn(async move {
|
delete_unloaded_items(
|
||||||
COMPONENT_PREVIEW_DB
|
alive_items,
|
||||||
.delete_unloaded_items(workspace_id, alive_items)
|
workspace_id,
|
||||||
.await
|
"component_previews",
|
||||||
})
|
&COMPONENT_PREVIEW_DB,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize(
|
fn serialize(
|
||||||
|
|
|
@ -47,30 +47,4 @@ impl ComponentPreviewDb {
|
||||||
WHERE item_id = ? AND workspace_id = ?
|
WHERE item_id = ? AND workspace_id = ?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_unloaded_items(
|
|
||||||
&self,
|
|
||||||
workspace: WorkspaceId,
|
|
||||||
alive_items: Vec<ItemId>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let placeholders = alive_items
|
|
||||||
.iter()
|
|
||||||
.map(|_| "?")
|
|
||||||
.collect::<Vec<&str>>()
|
|
||||||
.join(", ");
|
|
||||||
|
|
||||||
let query = format!(
|
|
||||||
"DELETE FROM component_previews WHERE workspace_id = ? AND item_id NOT IN ({placeholders})"
|
|
||||||
);
|
|
||||||
|
|
||||||
self.write(move |conn| {
|
|
||||||
let mut statement = Statement::prepare(conn, query)?;
|
|
||||||
let mut next_index = statement.bind(&workspace, 1)?;
|
|
||||||
for id in alive_items {
|
|
||||||
next_index = statement.bind(&id, next_index)?;
|
|
||||||
}
|
|
||||||
statement.exec()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,9 +45,9 @@ pub static ALL_FILE_DB_FAILED: LazyLock<AtomicBool> = LazyLock::new(|| AtomicBoo
|
||||||
/// This will retry a couple times if there are failures. If opening fails once, the db directory
|
/// This will retry a couple times if there are failures. If opening fails once, the db directory
|
||||||
/// is moved to a backup folder and a new one is created. If that fails, a shared in memory db is created.
|
/// is moved to a backup folder and a new one is created. If that fails, a shared in memory db is created.
|
||||||
/// In either case, static variables are set so that the user can be notified.
|
/// In either case, static variables are set so that the user can be notified.
|
||||||
pub async fn open_db<M: Migrator + 'static>(db_dir: &Path, scope: &str) -> ThreadSafeConnection<M> {
|
pub async fn open_db<M: Migrator + 'static>(db_dir: &Path, scope: &str) -> ThreadSafeConnection {
|
||||||
if *ZED_STATELESS {
|
if *ZED_STATELESS {
|
||||||
return open_fallback_db().await;
|
return open_fallback_db::<M>().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let main_db_dir = db_dir.join(format!("0-{}", scope));
|
let main_db_dir = db_dir.join(format!("0-{}", scope));
|
||||||
|
@ -58,7 +58,7 @@ pub async fn open_db<M: Migrator + 'static>(db_dir: &Path, scope: &str) -> Threa
|
||||||
.context("Could not create db directory")
|
.context("Could not create db directory")
|
||||||
.log_err()?;
|
.log_err()?;
|
||||||
let db_path = main_db_dir.join(Path::new(DB_FILE_NAME));
|
let db_path = main_db_dir.join(Path::new(DB_FILE_NAME));
|
||||||
open_main_db(&db_path).await
|
open_main_db::<M>(&db_path).await
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -70,12 +70,12 @@ pub async fn open_db<M: Migrator + 'static>(db_dir: &Path, scope: &str) -> Threa
|
||||||
ALL_FILE_DB_FAILED.store(true, Ordering::Release);
|
ALL_FILE_DB_FAILED.store(true, Ordering::Release);
|
||||||
|
|
||||||
// If still failed, create an in memory db with a known name
|
// If still failed, create an in memory db with a known name
|
||||||
open_fallback_db().await
|
open_fallback_db::<M>().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn open_main_db<M: Migrator>(db_path: &Path) -> Option<ThreadSafeConnection<M>> {
|
async fn open_main_db<M: Migrator>(db_path: &Path) -> Option<ThreadSafeConnection> {
|
||||||
log::info!("Opening main db");
|
log::info!("Opening main db");
|
||||||
ThreadSafeConnection::<M>::builder(db_path.to_string_lossy().as_ref(), true)
|
ThreadSafeConnection::builder::<M>(db_path.to_string_lossy().as_ref(), true)
|
||||||
.with_db_initialization_query(DB_INITIALIZE_QUERY)
|
.with_db_initialization_query(DB_INITIALIZE_QUERY)
|
||||||
.with_connection_initialize_query(CONNECTION_INITIALIZE_QUERY)
|
.with_connection_initialize_query(CONNECTION_INITIALIZE_QUERY)
|
||||||
.build()
|
.build()
|
||||||
|
@ -83,9 +83,9 @@ async fn open_main_db<M: Migrator>(db_path: &Path) -> Option<ThreadSafeConnectio
|
||||||
.log_err()
|
.log_err()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn open_fallback_db<M: Migrator>() -> ThreadSafeConnection<M> {
|
async fn open_fallback_db<M: Migrator>() -> ThreadSafeConnection {
|
||||||
log::info!("Opening fallback db");
|
log::info!("Opening fallback db");
|
||||||
ThreadSafeConnection::<M>::builder(FALLBACK_DB_NAME, false)
|
ThreadSafeConnection::builder::<M>(FALLBACK_DB_NAME, false)
|
||||||
.with_db_initialization_query(DB_INITIALIZE_QUERY)
|
.with_db_initialization_query(DB_INITIALIZE_QUERY)
|
||||||
.with_connection_initialize_query(CONNECTION_INITIALIZE_QUERY)
|
.with_connection_initialize_query(CONNECTION_INITIALIZE_QUERY)
|
||||||
.build()
|
.build()
|
||||||
|
@ -96,10 +96,10 @@ async fn open_fallback_db<M: Migrator>() -> ThreadSafeConnection<M> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub async fn open_test_db<M: Migrator>(db_name: &str) -> ThreadSafeConnection<M> {
|
pub async fn open_test_db<M: Migrator>(db_name: &str) -> ThreadSafeConnection {
|
||||||
use sqlez::thread_safe_connection::locking_queue;
|
use sqlez::thread_safe_connection::locking_queue;
|
||||||
|
|
||||||
ThreadSafeConnection::<M>::builder(db_name, false)
|
ThreadSafeConnection::builder::<M>(db_name, false)
|
||||||
.with_db_initialization_query(DB_INITIALIZE_QUERY)
|
.with_db_initialization_query(DB_INITIALIZE_QUERY)
|
||||||
.with_connection_initialize_query(CONNECTION_INITIALIZE_QUERY)
|
.with_connection_initialize_query(CONNECTION_INITIALIZE_QUERY)
|
||||||
// Serialize queued writes via a mutex and run them synchronously
|
// Serialize queued writes via a mutex and run them synchronously
|
||||||
|
@ -113,10 +113,10 @@ pub async fn open_test_db<M: Migrator>(db_name: &str) -> ThreadSafeConnection<M>
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! define_connection {
|
macro_rules! define_connection {
|
||||||
(pub static ref $id:ident: $t:ident<()> = $migrations:expr; $($global:ident)?) => {
|
(pub static ref $id:ident: $t:ident<()> = $migrations:expr; $($global:ident)?) => {
|
||||||
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<$t>);
|
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection);
|
||||||
|
|
||||||
impl ::std::ops::Deref for $t {
|
impl ::std::ops::Deref for $t {
|
||||||
type Target = $crate::sqlez::thread_safe_connection::ThreadSafeConnection<$t>;
|
type Target = $crate::sqlez::thread_safe_connection::ThreadSafeConnection;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
|
@ -133,9 +133,16 @@ macro_rules! define_connection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl $t {
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
pub async fn open_test_db(name: &'static str) -> Self {
|
||||||
|
$t($crate::open_test_db::<$t>(name).await)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
|
pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
|
||||||
$t($crate::smol::block_on($crate::open_test_db(stringify!($id))))
|
$t($crate::smol::block_on($crate::open_test_db::<$t>(stringify!($id))))
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(not(any(test, feature = "test-support")))]
|
#[cfg(not(any(test, feature = "test-support")))]
|
||||||
|
@ -146,14 +153,14 @@ macro_rules! define_connection {
|
||||||
} else {
|
} else {
|
||||||
$crate::RELEASE_CHANNEL.dev_name()
|
$crate::RELEASE_CHANNEL.dev_name()
|
||||||
};
|
};
|
||||||
$t($crate::smol::block_on($crate::open_db(db_dir, scope)))
|
$t($crate::smol::block_on($crate::open_db::<$t>(db_dir, scope)))
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
(pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr; $($global:ident)?) => {
|
(pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr; $($global:ident)?) => {
|
||||||
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<( $($d),+, $t )>);
|
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection);
|
||||||
|
|
||||||
impl ::std::ops::Deref for $t {
|
impl ::std::ops::Deref for $t {
|
||||||
type Target = $crate::sqlez::thread_safe_connection::ThreadSafeConnection<($($d),+, $t)>;
|
type Target = $crate::sqlez::thread_safe_connection::ThreadSafeConnection;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
|
@ -172,7 +179,7 @@ macro_rules! define_connection {
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
|
pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
|
||||||
$t($crate::smol::block_on($crate::open_test_db(stringify!($id))))
|
$t($crate::smol::block_on($crate::open_test_db::<($($d),+, $t)>(stringify!($id))))
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(not(any(test, feature = "test-support")))]
|
#[cfg(not(any(test, feature = "test-support")))]
|
||||||
|
@ -183,7 +190,7 @@ macro_rules! define_connection {
|
||||||
} else {
|
} else {
|
||||||
$crate::RELEASE_CHANNEL.dev_name()
|
$crate::RELEASE_CHANNEL.dev_name()
|
||||||
};
|
};
|
||||||
$t($crate::smol::block_on($crate::open_db(db_dir, scope)))
|
$t($crate::smol::block_on($crate::open_db::<($($d),+, $t)>(db_dir, scope)))
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_kvp() {
|
async fn test_kvp() {
|
||||||
let db = KeyValueStore(crate::open_test_db("test_kvp").await);
|
let db = KeyValueStore::open_test_db("test_kvp").await;
|
||||||
|
|
||||||
assert_eq!(db.read_kvp("key-1").unwrap(), None);
|
assert_eq!(db.read_kvp("key-1").unwrap(), None);
|
||||||
|
|
||||||
|
|
|
@ -1014,12 +1014,10 @@ impl SerializableItem for Editor {
|
||||||
fn cleanup(
|
fn cleanup(
|
||||||
workspace_id: WorkspaceId,
|
workspace_id: WorkspaceId,
|
||||||
alive_items: Vec<ItemId>,
|
alive_items: Vec<ItemId>,
|
||||||
window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
window.spawn(cx, async move |_| {
|
workspace::delete_unloaded_items(alive_items, workspace_id, "editors", &DB, cx)
|
||||||
DB.delete_unloaded_items(workspace_id, alive_items).await
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize(
|
fn deserialize(
|
||||||
|
|
|
@ -373,32 +373,6 @@ VALUES {placeholders};
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_unloaded_items(
|
|
||||||
&self,
|
|
||||||
workspace: WorkspaceId,
|
|
||||||
alive_items: Vec<ItemId>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let placeholders = alive_items
|
|
||||||
.iter()
|
|
||||||
.map(|_| "?")
|
|
||||||
.collect::<Vec<&str>>()
|
|
||||||
.join(", ");
|
|
||||||
|
|
||||||
let query = format!(
|
|
||||||
"DELETE FROM editors WHERE workspace_id = ? AND item_id NOT IN ({placeholders})"
|
|
||||||
);
|
|
||||||
|
|
||||||
self.write(move |conn| {
|
|
||||||
let mut statement = Statement::prepare(conn, query)?;
|
|
||||||
let mut next_index = statement.bind(&workspace, 1)?;
|
|
||||||
for id in alive_items {
|
|
||||||
next_index = statement.bind(&id, next_index)?;
|
|
||||||
}
|
|
||||||
statement.exec()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -18,7 +18,7 @@ use theme::Theme;
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use util::paths::PathExt;
|
use util::paths::PathExt;
|
||||||
use workspace::{
|
use workspace::{
|
||||||
ItemId, ItemSettings, Pane, ToolbarItemLocation, Workspace, WorkspaceId,
|
ItemId, ItemSettings, Pane, ToolbarItemLocation, Workspace, WorkspaceId, delete_unloaded_items,
|
||||||
item::{BreadcrumbText, Item, ProjectItem, SerializableItem, TabContentParams},
|
item::{BreadcrumbText, Item, ProjectItem, SerializableItem, TabContentParams},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -241,14 +241,16 @@ impl SerializableItem for ImageView {
|
||||||
fn cleanup(
|
fn cleanup(
|
||||||
workspace_id: WorkspaceId,
|
workspace_id: WorkspaceId,
|
||||||
alive_items: Vec<ItemId>,
|
alive_items: Vec<ItemId>,
|
||||||
window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Task<gpui::Result<()>> {
|
) -> Task<gpui::Result<()>> {
|
||||||
window.spawn(cx, async move |_| {
|
delete_unloaded_items(
|
||||||
IMAGE_VIEWER
|
alive_items,
|
||||||
.delete_unloaded_items(workspace_id, alive_items)
|
workspace_id,
|
||||||
.await
|
"image_viewers",
|
||||||
})
|
&IMAGE_VIEWER,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize(
|
fn serialize(
|
||||||
|
@ -380,10 +382,9 @@ pub fn init(cx: &mut App) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod persistence {
|
mod persistence {
|
||||||
use anyhow::Result;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use db::{define_connection, query, sqlez::statement::Statement, sqlez_macros::sql};
|
use db::{define_connection, query, sqlez_macros::sql};
|
||||||
use workspace::{ItemId, WorkspaceDb, WorkspaceId};
|
use workspace::{ItemId, WorkspaceDb, WorkspaceId};
|
||||||
|
|
||||||
define_connection! {
|
define_connection! {
|
||||||
|
@ -421,31 +422,5 @@ mod persistence {
|
||||||
WHERE item_id = ? AND workspace_id = ?
|
WHERE item_id = ? AND workspace_id = ?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_unloaded_items(
|
|
||||||
&self,
|
|
||||||
workspace: WorkspaceId,
|
|
||||||
alive_items: Vec<ItemId>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let placeholders = alive_items
|
|
||||||
.iter()
|
|
||||||
.map(|_| "?")
|
|
||||||
.collect::<Vec<&str>>()
|
|
||||||
.join(", ");
|
|
||||||
|
|
||||||
let query = format!(
|
|
||||||
"DELETE FROM image_viewers WHERE workspace_id = ? AND item_id NOT IN ({placeholders})"
|
|
||||||
);
|
|
||||||
|
|
||||||
self.write(move |conn| {
|
|
||||||
let mut statement = Statement::prepare(conn, query)?;
|
|
||||||
let mut next_index = statement.bind(&workspace, 1)?;
|
|
||||||
for id in alive_items {
|
|
||||||
next_index = statement.bind(&id, next_index)?;
|
|
||||||
}
|
|
||||||
statement.exec()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,21 +27,22 @@ static QUEUES: LazyLock<RwLock<HashMap<Arc<str>, WriteQueue>>> = LazyLock::new(D
|
||||||
/// Thread safe connection to a given database file or in memory db. This can be cloned, shared, static,
|
/// Thread safe connection to a given database file or in memory db. This can be cloned, shared, static,
|
||||||
/// whatever. It derefs to a synchronous connection by thread that is read only. A write capable connection
|
/// whatever. It derefs to a synchronous connection by thread that is read only. A write capable connection
|
||||||
/// may be accessed by passing a callback to the `write` function which will queue the callback
|
/// may be accessed by passing a callback to the `write` function which will queue the callback
|
||||||
pub struct ThreadSafeConnection<M: Migrator + 'static = ()> {
|
#[derive(Clone)]
|
||||||
|
pub struct ThreadSafeConnection {
|
||||||
uri: Arc<str>,
|
uri: Arc<str>,
|
||||||
persistent: bool,
|
persistent: bool,
|
||||||
connection_initialize_query: Option<&'static str>,
|
connection_initialize_query: Option<&'static str>,
|
||||||
connections: Arc<ThreadLocal<Connection>>,
|
connections: Arc<ThreadLocal<Connection>>,
|
||||||
_migrator: PhantomData<*mut M>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<M: Migrator> Send for ThreadSafeConnection<M> {}
|
unsafe impl Send for ThreadSafeConnection {}
|
||||||
unsafe impl<M: Migrator> Sync for ThreadSafeConnection<M> {}
|
unsafe impl Sync for ThreadSafeConnection {}
|
||||||
|
|
||||||
pub struct ThreadSafeConnectionBuilder<M: Migrator + 'static = ()> {
|
pub struct ThreadSafeConnectionBuilder<M: Migrator + 'static = ()> {
|
||||||
db_initialize_query: Option<&'static str>,
|
db_initialize_query: Option<&'static str>,
|
||||||
write_queue_constructor: Option<WriteQueueConstructor>,
|
write_queue_constructor: Option<WriteQueueConstructor>,
|
||||||
connection: ThreadSafeConnection<M>,
|
connection: ThreadSafeConnection,
|
||||||
|
_migrator: PhantomData<*mut M>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Migrator> ThreadSafeConnectionBuilder<M> {
|
impl<M: Migrator> ThreadSafeConnectionBuilder<M> {
|
||||||
|
@ -72,7 +73,7 @@ impl<M: Migrator> ThreadSafeConnectionBuilder<M> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn build(self) -> anyhow::Result<ThreadSafeConnection<M>> {
|
pub async fn build(self) -> anyhow::Result<ThreadSafeConnection> {
|
||||||
self.connection
|
self.connection
|
||||||
.initialize_queues(self.write_queue_constructor);
|
.initialize_queues(self.write_queue_constructor);
|
||||||
|
|
||||||
|
@ -111,7 +112,7 @@ impl<M: Migrator> ThreadSafeConnectionBuilder<M> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Migrator> ThreadSafeConnection<M> {
|
impl ThreadSafeConnection {
|
||||||
fn initialize_queues(&self, write_queue_constructor: Option<WriteQueueConstructor>) -> bool {
|
fn initialize_queues(&self, write_queue_constructor: Option<WriteQueueConstructor>) -> bool {
|
||||||
if !QUEUES.read().contains_key(&self.uri) {
|
if !QUEUES.read().contains_key(&self.uri) {
|
||||||
let mut queues = QUEUES.write();
|
let mut queues = QUEUES.write();
|
||||||
|
@ -125,7 +126,7 @@ impl<M: Migrator> ThreadSafeConnection<M> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn builder(uri: &str, persistent: bool) -> ThreadSafeConnectionBuilder<M> {
|
pub fn builder<M: Migrator>(uri: &str, persistent: bool) -> ThreadSafeConnectionBuilder<M> {
|
||||||
ThreadSafeConnectionBuilder::<M> {
|
ThreadSafeConnectionBuilder::<M> {
|
||||||
db_initialize_query: None,
|
db_initialize_query: None,
|
||||||
write_queue_constructor: None,
|
write_queue_constructor: None,
|
||||||
|
@ -134,8 +135,8 @@ impl<M: Migrator> ThreadSafeConnection<M> {
|
||||||
persistent,
|
persistent,
|
||||||
connection_initialize_query: None,
|
connection_initialize_query: None,
|
||||||
connections: Default::default(),
|
connections: Default::default(),
|
||||||
_migrator: PhantomData,
|
|
||||||
},
|
},
|
||||||
|
_migrator: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +201,7 @@ impl<M: Migrator> ThreadSafeConnection<M> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadSafeConnection<()> {
|
impl ThreadSafeConnection {
|
||||||
/// Special constructor for ThreadSafeConnection which disallows db initialization and migrations.
|
/// Special constructor for ThreadSafeConnection which disallows db initialization and migrations.
|
||||||
/// This allows construction to be infallible and not write to the db.
|
/// This allows construction to be infallible and not write to the db.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
@ -214,7 +215,6 @@ impl ThreadSafeConnection<()> {
|
||||||
persistent,
|
persistent,
|
||||||
connection_initialize_query,
|
connection_initialize_query,
|
||||||
connections: Default::default(),
|
connections: Default::default(),
|
||||||
_migrator: PhantomData,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
connection.initialize_queues(write_queue_constructor);
|
connection.initialize_queues(write_queue_constructor);
|
||||||
|
@ -222,19 +222,7 @@ impl ThreadSafeConnection<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Migrator> Clone for ThreadSafeConnection<M> {
|
impl Deref for ThreadSafeConnection {
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
uri: self.uri.clone(),
|
|
||||||
persistent: self.persistent,
|
|
||||||
connection_initialize_query: self.connection_initialize_query,
|
|
||||||
connections: self.connections.clone(),
|
|
||||||
_migrator: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M: Migrator> Deref for ThreadSafeConnection<M> {
|
|
||||||
type Target = Connection;
|
type Target = Connection;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
|
@ -301,7 +289,7 @@ mod test {
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
handles.push(thread::spawn(|| {
|
handles.push(thread::spawn(|| {
|
||||||
let builder =
|
let builder =
|
||||||
ThreadSafeConnection::<TestDomain>::builder("annoying-test.db", false)
|
ThreadSafeConnection::builder::<TestDomain>("annoying-test.db", false)
|
||||||
.with_db_initialization_query("PRAGMA journal_mode=WAL")
|
.with_db_initialization_query("PRAGMA journal_mode=WAL")
|
||||||
.with_connection_initialize_query(indoc! {"
|
.with_connection_initialize_query(indoc! {"
|
||||||
PRAGMA synchronous=NORMAL;
|
PRAGMA synchronous=NORMAL;
|
||||||
|
@ -353,7 +341,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
let builder =
|
let builder =
|
||||||
ThreadSafeConnection::<TestWorkspace>::builder("wild_zed_lost_failure", false)
|
ThreadSafeConnection::builder::<TestWorkspace>("wild_zed_lost_failure", false)
|
||||||
.with_connection_initialize_query("PRAGMA FOREIGN_KEYS=true");
|
.with_connection_initialize_query("PRAGMA FOREIGN_KEYS=true");
|
||||||
|
|
||||||
smol::block_on(builder.build()).unwrap();
|
smol::block_on(builder.build()).unwrap();
|
||||||
|
|
|
@ -459,30 +459,4 @@ impl TerminalDb {
|
||||||
WHERE item_id = ? AND workspace_id = ?
|
WHERE item_id = ? AND workspace_id = ?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_unloaded_items(
|
|
||||||
&self,
|
|
||||||
workspace: WorkspaceId,
|
|
||||||
alive_items: Vec<ItemId>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let placeholders = alive_items
|
|
||||||
.iter()
|
|
||||||
.map(|_| "?")
|
|
||||||
.collect::<Vec<&str>>()
|
|
||||||
.join(", ");
|
|
||||||
|
|
||||||
let query = format!(
|
|
||||||
"DELETE FROM terminals WHERE workspace_id = ? AND item_id NOT IN ({placeholders})"
|
|
||||||
);
|
|
||||||
|
|
||||||
self.write(move |conn| {
|
|
||||||
let mut statement = Statement::prepare(conn, query)?;
|
|
||||||
let mut next_index = statement.bind(&workspace, 1)?;
|
|
||||||
for id in alive_items {
|
|
||||||
next_index = statement.bind(&id, next_index)?;
|
|
||||||
}
|
|
||||||
statement.exec()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ use ui::{
|
||||||
use util::{ResultExt, debug_panic, paths::PathWithPosition};
|
use util::{ResultExt, debug_panic, paths::PathWithPosition};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
CloseActiveItem, NewCenterTerminal, NewTerminal, OpenOptions, OpenVisible, ToolbarItemLocation,
|
CloseActiveItem, NewCenterTerminal, NewTerminal, OpenOptions, OpenVisible, ToolbarItemLocation,
|
||||||
Workspace, WorkspaceId,
|
Workspace, WorkspaceId, delete_unloaded_items,
|
||||||
item::{
|
item::{
|
||||||
BreadcrumbText, Item, ItemEvent, SerializableItem, TabContentParams, TabTooltipContent,
|
BreadcrumbText, Item, ItemEvent, SerializableItem, TabContentParams, TabTooltipContent,
|
||||||
},
|
},
|
||||||
|
@ -1582,14 +1582,10 @@ impl SerializableItem for TerminalView {
|
||||||
fn cleanup(
|
fn cleanup(
|
||||||
workspace_id: WorkspaceId,
|
workspace_id: WorkspaceId,
|
||||||
alive_items: Vec<workspace::ItemId>,
|
alive_items: Vec<workspace::ItemId>,
|
||||||
window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Task<gpui::Result<()>> {
|
) -> Task<gpui::Result<()>> {
|
||||||
window.spawn(cx, async move |_| {
|
delete_unloaded_items(alive_items, workspace_id, "terminals", &TERMINAL_DB, cx)
|
||||||
TERMINAL_DB
|
|
||||||
.delete_unloaded_items(workspace_id, alive_items)
|
|
||||||
.await
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize(
|
fn serialize(
|
||||||
|
|
|
@ -11,7 +11,7 @@ use std::{
|
||||||
use anyhow::{Context, Result, anyhow, bail};
|
use anyhow::{Context, Result, anyhow, bail};
|
||||||
use client::DevServerProjectId;
|
use client::DevServerProjectId;
|
||||||
use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
|
use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
|
||||||
use gpui::{Axis, Bounds, WindowBounds, WindowId, point, size};
|
use gpui::{Axis, Bounds, Task, WindowBounds, WindowId, point, size};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use project::debugger::breakpoint_store::{BreakpointState, SourceBreakpoint};
|
use project::debugger::breakpoint_store::{BreakpointState, SourceBreakpoint};
|
||||||
|
|
||||||
|
@ -21,16 +21,17 @@ use remote::ssh_session::SshProjectId;
|
||||||
use sqlez::{
|
use sqlez::{
|
||||||
bindable::{Bind, Column, StaticColumnCount},
|
bindable::{Bind, Column, StaticColumnCount},
|
||||||
statement::{SqlType, Statement},
|
statement::{SqlType, Statement},
|
||||||
|
thread_safe_connection::ThreadSafeConnection,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ui::px;
|
use ui::{App, px};
|
||||||
use util::{ResultExt, maybe};
|
use util::{ResultExt, maybe};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::WorkspaceId;
|
use crate::WorkspaceId;
|
||||||
|
|
||||||
use model::{
|
use model::{
|
||||||
GroupId, LocalPaths, PaneId, SerializedItem, SerializedPane, SerializedPaneGroup,
|
GroupId, ItemId, LocalPaths, PaneId, SerializedItem, SerializedPane, SerializedPaneGroup,
|
||||||
SerializedSshProject, SerializedWorkspace,
|
SerializedSshProject, SerializedWorkspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1422,6 +1423,37 @@ impl WorkspaceDb {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn delete_unloaded_items(
|
||||||
|
alive_items: Vec<ItemId>,
|
||||||
|
workspace_id: WorkspaceId,
|
||||||
|
table: &'static str,
|
||||||
|
db: &ThreadSafeConnection,
|
||||||
|
cx: &mut App,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
|
let db = db.clone();
|
||||||
|
cx.spawn(async move |_| {
|
||||||
|
let placeholders = alive_items
|
||||||
|
.iter()
|
||||||
|
.map(|_| "?")
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
let query = format!(
|
||||||
|
"DELETE FROM {table} WHERE workspace_id = ? AND item_id NOT IN ({placeholders})"
|
||||||
|
);
|
||||||
|
|
||||||
|
db.write(move |conn| {
|
||||||
|
let mut statement = Statement::prepare(conn, query)?;
|
||||||
|
let mut next_index = statement.bind(&workspace_id, 1)?;
|
||||||
|
for id in alive_items {
|
||||||
|
next_index = statement.bind(&id, next_index)?;
|
||||||
|
}
|
||||||
|
statement.exec()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -1430,14 +1462,13 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::persistence::model::SerializedWorkspace;
|
use crate::persistence::model::SerializedWorkspace;
|
||||||
use crate::persistence::model::{SerializedItem, SerializedPane, SerializedPaneGroup};
|
use crate::persistence::model::{SerializedItem, SerializedPane, SerializedPaneGroup};
|
||||||
use db::open_test_db;
|
|
||||||
use gpui;
|
use gpui;
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_breakpoints() {
|
async fn test_breakpoints() {
|
||||||
env_logger::try_init().ok();
|
env_logger::try_init().ok();
|
||||||
|
|
||||||
let db = WorkspaceDb(open_test_db("test_breakpoints").await);
|
let db = WorkspaceDb::open_test_db("test_breakpoints").await;
|
||||||
let id = db.next_id().await.unwrap();
|
let id = db.next_id().await.unwrap();
|
||||||
|
|
||||||
let path = Path::new("/tmp/test.rs");
|
let path = Path::new("/tmp/test.rs");
|
||||||
|
@ -1622,7 +1653,7 @@ mod tests {
|
||||||
async fn test_remove_last_breakpoint() {
|
async fn test_remove_last_breakpoint() {
|
||||||
env_logger::try_init().ok();
|
env_logger::try_init().ok();
|
||||||
|
|
||||||
let db = WorkspaceDb(open_test_db("test_remove_last_breakpoint").await);
|
let db = WorkspaceDb::open_test_db("test_remove_last_breakpoint").await;
|
||||||
let id = db.next_id().await.unwrap();
|
let id = db.next_id().await.unwrap();
|
||||||
|
|
||||||
let singular_path = Path::new("/tmp/test_remove_last_breakpoint.rs");
|
let singular_path = Path::new("/tmp/test_remove_last_breakpoint.rs");
|
||||||
|
@ -1709,7 +1740,7 @@ mod tests {
|
||||||
async fn test_next_id_stability() {
|
async fn test_next_id_stability() {
|
||||||
env_logger::try_init().ok();
|
env_logger::try_init().ok();
|
||||||
|
|
||||||
let db = WorkspaceDb(open_test_db("test_next_id_stability").await);
|
let db = WorkspaceDb::open_test_db("test_next_id_stability").await;
|
||||||
|
|
||||||
db.write(|conn| {
|
db.write(|conn| {
|
||||||
conn.migrate(
|
conn.migrate(
|
||||||
|
@ -1757,7 +1788,7 @@ mod tests {
|
||||||
async fn test_workspace_id_stability() {
|
async fn test_workspace_id_stability() {
|
||||||
env_logger::try_init().ok();
|
env_logger::try_init().ok();
|
||||||
|
|
||||||
let db = WorkspaceDb(open_test_db("test_workspace_id_stability").await);
|
let db = WorkspaceDb::open_test_db("test_workspace_id_stability").await;
|
||||||
|
|
||||||
db.write(|conn| {
|
db.write(|conn| {
|
||||||
conn.migrate(
|
conn.migrate(
|
||||||
|
@ -1851,7 +1882,7 @@ mod tests {
|
||||||
async fn test_full_workspace_serialization() {
|
async fn test_full_workspace_serialization() {
|
||||||
env_logger::try_init().ok();
|
env_logger::try_init().ok();
|
||||||
|
|
||||||
let db = WorkspaceDb(open_test_db("test_full_workspace_serialization").await);
|
let db = WorkspaceDb::open_test_db("test_full_workspace_serialization").await;
|
||||||
|
|
||||||
// -----------------
|
// -----------------
|
||||||
// | 1,2 | 5,6 |
|
// | 1,2 | 5,6 |
|
||||||
|
@ -1926,7 +1957,7 @@ mod tests {
|
||||||
async fn test_workspace_assignment() {
|
async fn test_workspace_assignment() {
|
||||||
env_logger::try_init().ok();
|
env_logger::try_init().ok();
|
||||||
|
|
||||||
let db = WorkspaceDb(open_test_db("test_basic_functionality").await);
|
let db = WorkspaceDb::open_test_db("test_basic_functionality").await;
|
||||||
|
|
||||||
let workspace_1 = SerializedWorkspace {
|
let workspace_1 = SerializedWorkspace {
|
||||||
id: WorkspaceId(1),
|
id: WorkspaceId(1),
|
||||||
|
@ -2022,7 +2053,7 @@ mod tests {
|
||||||
async fn test_session_workspaces() {
|
async fn test_session_workspaces() {
|
||||||
env_logger::try_init().ok();
|
env_logger::try_init().ok();
|
||||||
|
|
||||||
let db = WorkspaceDb(open_test_db("test_serializing_workspaces_session_id").await);
|
let db = WorkspaceDb::open_test_db("test_serializing_workspaces_session_id").await;
|
||||||
|
|
||||||
let workspace_1 = SerializedWorkspace {
|
let workspace_1 = SerializedWorkspace {
|
||||||
id: WorkspaceId(1),
|
id: WorkspaceId(1),
|
||||||
|
@ -2175,7 +2206,7 @@ mod tests {
|
||||||
let dir4 = tempfile::TempDir::with_prefix("dir4").unwrap();
|
let dir4 = tempfile::TempDir::with_prefix("dir4").unwrap();
|
||||||
|
|
||||||
let db =
|
let db =
|
||||||
WorkspaceDb(open_test_db("test_serializing_workspaces_last_session_workspaces").await);
|
WorkspaceDb::open_test_db("test_serializing_workspaces_last_session_workspaces").await;
|
||||||
|
|
||||||
let workspaces = [
|
let workspaces = [
|
||||||
(1, vec![dir1.path()], vec![0], 9),
|
(1, vec![dir1.path()], vec![0], 9),
|
||||||
|
@ -2264,9 +2295,10 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_last_session_workspace_locations_ssh_projects() {
|
async fn test_last_session_workspace_locations_ssh_projects() {
|
||||||
let db = WorkspaceDb(
|
let db = WorkspaceDb::open_test_db(
|
||||||
open_test_db("test_serializing_workspaces_last_session_workspaces_ssh_projects").await,
|
"test_serializing_workspaces_last_session_workspaces_ssh_projects",
|
||||||
);
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let ssh_projects = [
|
let ssh_projects = [
|
||||||
("host-1", "my-user-1"),
|
("host-1", "my-user-1"),
|
||||||
|
@ -2340,7 +2372,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_get_or_create_ssh_project() {
|
async fn test_get_or_create_ssh_project() {
|
||||||
let db = WorkspaceDb(open_test_db("test_get_or_create_ssh_project").await);
|
let db = WorkspaceDb::open_test_db("test_get_or_create_ssh_project").await;
|
||||||
|
|
||||||
let (host, port, paths, user) = (
|
let (host, port, paths, user) = (
|
||||||
"example.com".to_string(),
|
"example.com".to_string(),
|
||||||
|
@ -2386,7 +2418,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_get_or_create_ssh_project_with_null_user() {
|
async fn test_get_or_create_ssh_project_with_null_user() {
|
||||||
let db = WorkspaceDb(open_test_db("test_get_or_create_ssh_project_with_null_user").await);
|
let db = WorkspaceDb::open_test_db("test_get_or_create_ssh_project_with_null_user").await;
|
||||||
|
|
||||||
let (host, port, paths, user) = (
|
let (host, port, paths, user) = (
|
||||||
"example.com".to_string(),
|
"example.com".to_string(),
|
||||||
|
@ -2415,7 +2447,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_get_ssh_projects() {
|
async fn test_get_ssh_projects() {
|
||||||
let db = WorkspaceDb(open_test_db("test_get_ssh_projects").await);
|
let db = WorkspaceDb::open_test_db("test_get_ssh_projects").await;
|
||||||
|
|
||||||
let projects = vec![
|
let projects = vec![
|
||||||
(
|
(
|
||||||
|
@ -2458,7 +2490,7 @@ mod tests {
|
||||||
async fn test_simple_split() {
|
async fn test_simple_split() {
|
||||||
env_logger::try_init().ok();
|
env_logger::try_init().ok();
|
||||||
|
|
||||||
let db = WorkspaceDb(open_test_db("simple_split").await);
|
let db = WorkspaceDb::open_test_db("simple_split").await;
|
||||||
|
|
||||||
// -----------------
|
// -----------------
|
||||||
// | 1,2 | 5,6 |
|
// | 1,2 | 5,6 |
|
||||||
|
@ -2513,7 +2545,7 @@ mod tests {
|
||||||
async fn test_cleanup_panes() {
|
async fn test_cleanup_panes() {
|
||||||
env_logger::try_init().ok();
|
env_logger::try_init().ok();
|
||||||
|
|
||||||
let db = WorkspaceDb(open_test_db("test_cleanup_panes").await);
|
let db = WorkspaceDb::open_test_db("test_cleanup_panes").await;
|
||||||
|
|
||||||
let center_pane = group(
|
let center_pane = group(
|
||||||
Axis::Horizontal,
|
Axis::Horizontal,
|
||||||
|
|
|
@ -62,7 +62,7 @@ use persistence::{
|
||||||
model::{SerializedSshProject, SerializedWorkspace},
|
model::{SerializedSshProject, SerializedWorkspace},
|
||||||
};
|
};
|
||||||
pub use persistence::{
|
pub use persistence::{
|
||||||
DB as WORKSPACE_DB, WorkspaceDb,
|
DB as WORKSPACE_DB, WorkspaceDb, delete_unloaded_items,
|
||||||
model::{ItemId, LocalPaths, SerializedWorkspaceLocation},
|
model::{ItemId, LocalPaths, SerializedWorkspaceLocation},
|
||||||
};
|
};
|
||||||
use postage::stream::Stream;
|
use postage::stream::Stream;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue