mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-24 17:28:40 +00:00
Added UUID based, stable workspace ID for caching on item startup. Completed first sketch of terminal persistence. Still need to debug it though....
This commit is contained in:
parent
e659823e6c
commit
a47f2ca445
20 changed files with 501 additions and 364 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1572,6 +1572,7 @@ dependencies = [
|
|||
"sqlez",
|
||||
"tempdir",
|
||||
"util",
|
||||
"uuid 1.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6834,6 +6835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -22,6 +22,7 @@ lazy_static = "1.4.0"
|
|||
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||
parking_lot = "0.11.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
uuid = { version = "1.2.2", features = ["v4", "fast-rng"] }
|
||||
|
||||
[dev-dependencies]
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
pub mod kvp;
|
||||
|
||||
// Re-export indoc and sqlez so clients only need to include us
|
||||
// Re-export
|
||||
pub use anyhow;
|
||||
pub use indoc::indoc;
|
||||
pub use lazy_static;
|
||||
pub use sqlez;
|
||||
|
||||
use std::fs::{create_dir_all, remove_dir_all};
|
||||
use std::path::Path;
|
||||
use sqlez::bindable::{Bind, Column};
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use anyhow::Result;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use sqlez::connection::Connection;
|
||||
use sqlez::domain::{Domain, Migrator};
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use sqlez::domain::Domain;
|
||||
|
||||
use sqlez::domain::Migrator;
|
||||
use sqlez::thread_safe_connection::ThreadSafeConnection;
|
||||
use std::fs::{create_dir_all, remove_dir_all};
|
||||
use std::path::Path;
|
||||
use util::channel::{ReleaseChannel, RELEASE_CHANNEL, RELEASE_CHANNEL_NAME};
|
||||
use util::paths::DB_DIR;
|
||||
use uuid::Uuid as RealUuid;
|
||||
|
||||
const INITIALIZE_QUERY: &'static str = indoc! {"
|
||||
PRAGMA journal_mode=WAL;
|
||||
|
@ -25,6 +30,47 @@ const INITIALIZE_QUERY: &'static str = indoc! {"
|
|||
PRAGMA case_sensitive_like=TRUE;
|
||||
"};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Uuid(RealUuid);
|
||||
|
||||
impl std::ops::Deref for Uuid {
|
||||
type Target = RealUuid;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Bind for Uuid {
|
||||
fn bind(
|
||||
&self,
|
||||
statement: &sqlez::statement::Statement,
|
||||
start_index: i32,
|
||||
) -> anyhow::Result<i32> {
|
||||
statement.bind(self.as_bytes(), start_index)
|
||||
}
|
||||
}
|
||||
|
||||
impl Column for Uuid {
|
||||
fn column(
|
||||
statement: &mut sqlez::statement::Statement,
|
||||
start_index: i32,
|
||||
) -> anyhow::Result<(Self, i32)> {
|
||||
let blob = statement.column_blob(start_index)?;
|
||||
Ok((Uuid::from_bytes(blob)?, start_index + 1))
|
||||
}
|
||||
}
|
||||
|
||||
impl Uuid {
|
||||
pub fn new() -> Self {
|
||||
Uuid(RealUuid::new_v4())
|
||||
}
|
||||
|
||||
fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
|
||||
Ok(Uuid(RealUuid::from_bytes(bytes.try_into()?)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Open or create a database at the given directory path.
|
||||
pub fn open_file_db<M: Migrator>() -> ThreadSafeConnection<M> {
|
||||
// Use 0 for now. Will implement incrementing and clearing of old db files soon TM
|
||||
|
@ -77,3 +123,88 @@ macro_rules! connection {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! exec_method {
|
||||
($id:ident(): $sql:literal) => {
|
||||
pub fn $id(&self) -> $crate::sqlez::anyhow::Result<()> {
|
||||
use $crate::anyhow::Context;
|
||||
|
||||
self.exec($sql)?()
|
||||
.context(::std::format!(
|
||||
"Error in {}, exec failed to execute or parse for: {}",
|
||||
::std::stringify!($id),
|
||||
::std::stringify!($sql),
|
||||
))
|
||||
}
|
||||
};
|
||||
($id:ident($($arg:ident: $arg_type:ty),+): $sql:literal) => {
|
||||
pub fn $id(&self, $($arg: $arg_type),+) -> $crate::sqlez::anyhow::Result<()> {
|
||||
use $crate::anyhow::Context;
|
||||
|
||||
self.exec_bound::<($($arg_type),+)>($sql)?(($($arg),+))
|
||||
.context(::std::format!(
|
||||
"Error in {}, exec_bound failed to execute or parse for: {}",
|
||||
::std::stringify!($id),
|
||||
::std::stringify!($sql),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! select_method {
|
||||
($id:ident() -> $return_type:ty: $sql:literal) => {
|
||||
pub fn $id(&self) -> $crate::sqlez::anyhow::Result<Vec<$return_type>> {
|
||||
use $crate::anyhow::Context;
|
||||
|
||||
self.select::<$return_type>($sql)?(())
|
||||
.context(::std::format!(
|
||||
"Error in {}, select_row failed to execute or parse for: {}",
|
||||
::std::stringify!($id),
|
||||
::std::stringify!($sql),
|
||||
))
|
||||
}
|
||||
};
|
||||
($id:ident($($arg:ident: $arg_type:ty),+) -> $return_type:ty: $sql:literal) => {
|
||||
pub fn $id(&self, $($arg: $arg_type),+) -> $crate::sqlez::anyhow::Result<Vec<$return_type>> {
|
||||
use $crate::anyhow::Context;
|
||||
|
||||
self.select_bound::<($($arg_type),+), $return_type>($sql)?(($($arg),+))
|
||||
.context(::std::format!(
|
||||
"Error in {}, exec_bound failed to execute or parse for: {}",
|
||||
::std::stringify!($id),
|
||||
::std::stringify!($sql),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! select_row_method {
|
||||
($id:ident() -> $return_type:ty: $sql:literal) => {
|
||||
pub fn $id(&self) -> $crate::sqlez::anyhow::Result<Option<$return_type>> {
|
||||
use $crate::anyhow::Context;
|
||||
|
||||
self.select_row::<$return_type>($sql)?(())
|
||||
.context(::std::format!(
|
||||
"Error in {}, select_row failed to execute or parse for: {}",
|
||||
::std::stringify!($id),
|
||||
::std::stringify!($sql),
|
||||
))
|
||||
}
|
||||
};
|
||||
($id:ident($($arg:ident: $arg_type:ty),+) -> $return_type:ty: $sql:literal) => {
|
||||
pub fn $id(&self, $($arg: $arg_type),+) -> $crate::sqlez::anyhow::Result<Option<$return_type>> {
|
||||
use $crate::anyhow::Context;
|
||||
|
||||
self.select_row_bound::<($($arg_type),+), $return_type>($sql)?(($($arg),+))
|
||||
.context(::std::format!(
|
||||
"Error in {}, select_row_bound failed to execute or parse for: {}",
|
||||
::std::stringify!($id),
|
||||
::std::stringify!($sql),
|
||||
))
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -584,7 +584,11 @@ impl Item for ProjectDiagnosticsEditor {
|
|||
});
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
_workspace_id: workspace::WorkspaceId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
|
|
|
@ -83,7 +83,7 @@ use theme::{DiagnosticStyle, Theme};
|
|||
use util::{post_inc, ResultExt, TryFutureExt};
|
||||
use workspace::{ItemNavHistory, Workspace};
|
||||
|
||||
use crate::{git::diff_hunk_to_display, persistence::DB};
|
||||
use crate::git::diff_hunk_to_display;
|
||||
|
||||
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
||||
const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
|
||||
|
@ -1137,30 +1137,30 @@ impl Editor {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||
if let Some(project) = project.as_ref() {
|
||||
if let Some(file) = buffer
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.and_then(|buffer| buffer.read(cx).file())
|
||||
.and_then(|file| file.as_local())
|
||||
{
|
||||
let item_id = cx.weak_handle().id();
|
||||
let workspace_id = project
|
||||
.read(cx)
|
||||
.visible_worktrees(cx)
|
||||
.map(|worktree| worktree.read(cx).abs_path())
|
||||
.collect::<Vec<_>>()
|
||||
.into();
|
||||
let path = file.abs_path(cx);
|
||||
dbg!(&path);
|
||||
// if let Some(project) = project.as_ref() {
|
||||
// if let Some(file) = buffer
|
||||
// .read(cx)
|
||||
// .as_singleton()
|
||||
// .and_then(|buffer| buffer.read(cx).file())
|
||||
// .and_then(|file| file.as_local())
|
||||
// {
|
||||
// // let item_id = cx.weak_handle().id();
|
||||
// // let workspace_id = project
|
||||
// // .read(cx)
|
||||
// // .visible_worktrees(cx)
|
||||
// // .map(|worktree| worktree.read(cx).abs_path())
|
||||
// // .collect::<Vec<_>>()
|
||||
// // .into();
|
||||
// let path = file.abs_path(cx);
|
||||
// dbg!(&path);
|
||||
|
||||
cx.background()
|
||||
.spawn(async move {
|
||||
DB.save_path(item_id, workspace_id, path).log_err();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
// // cx.background()
|
||||
// // .spawn(async move {
|
||||
// // DB.save_path(item_id, workspace_id, path).log_err();
|
||||
// // })
|
||||
// // .detach();
|
||||
// }
|
||||
// }
|
||||
|
||||
Self::new(EditorMode::Full, buffer, project, None, cx)
|
||||
}
|
||||
|
|
|
@ -368,7 +368,7 @@ impl Item for Editor {
|
|||
self.buffer.read(cx).is_singleton()
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>
|
||||
fn clone_on_split(&self, _workspace_id: WorkspaceId, cx: &mut ViewContext<Self>) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
|
@ -561,14 +561,13 @@ impl Item for Editor {
|
|||
fn deserialize(
|
||||
project: ModelHandle<Project>,
|
||||
_workspace: WeakViewHandle<Workspace>,
|
||||
workspace_id: WorkspaceId,
|
||||
workspace_id: workspace::WorkspaceId,
|
||||
item_id: ItemId,
|
||||
cx: &mut ViewContext<Pane>,
|
||||
) -> Task<Result<ViewHandle<Self>>> {
|
||||
if let Some(project_item) = project.update(cx, |project, cx| {
|
||||
// Look up the path with this key associated, create a self with that path
|
||||
let path = DB.get_path(item_id, workspace_id).ok()?;
|
||||
dbg!(&path);
|
||||
let (worktree, path) = project.find_local_worktree(&path, cx)?;
|
||||
let project_path = ProjectPath {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use db::connection;
|
||||
use db::{connection, exec_method};
|
||||
use indoc::indoc;
|
||||
use sqlez::domain::Domain;
|
||||
use workspace::{ItemId, Workspace, WorkspaceId};
|
||||
|
@ -35,18 +35,12 @@ impl EditorDb {
|
|||
pub fn get_path(&self, item_id: ItemId, workspace_id: WorkspaceId) -> Result<PathBuf> {
|
||||
self.select_row_bound(indoc! {"
|
||||
SELECT path FROM editors
|
||||
WHERE item_id = ? AND workspace_id = ?"})?((item_id, &workspace_id))?
|
||||
WHERE item_id = ? AND workspace_id = ?"})?((item_id, workspace_id))?
|
||||
.context("Path not found for serialized editor")
|
||||
}
|
||||
|
||||
pub fn save_path(
|
||||
&self,
|
||||
item_id: ItemId,
|
||||
workspace_id: WorkspaceId,
|
||||
path: PathBuf,
|
||||
) -> Result<()> {
|
||||
self.exec_bound::<(ItemId, &WorkspaceId, &Path)>(indoc! {"
|
||||
INSERT OR REPLACE INTO editors(item_id, workspace_id, path)
|
||||
VALUES (?, ?, ?)"})?((item_id, &workspace_id, &path))
|
||||
}
|
||||
exec_method!(save_path(item_id: ItemId, workspace_id: WorkspaceId, path: &Path):
|
||||
"INSERT OR REPLACE INTO editors(item_id, workspace_id, path)
|
||||
VALUES (?, ?, ?)"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ use util::ResultExt as _;
|
|||
use workspace::{
|
||||
item::{Item, ItemEvent, ItemHandle},
|
||||
searchable::{Direction, SearchableItem, SearchableItemHandle},
|
||||
ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, Workspace,
|
||||
ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, Workspace, WorkspaceId,
|
||||
};
|
||||
|
||||
actions!(project_search, [SearchInNew, ToggleFocus]);
|
||||
|
@ -315,7 +315,7 @@ impl Item for ProjectSearchView {
|
|||
.update(cx, |editor, cx| editor.reload(project, cx))
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>
|
||||
fn clone_on_split(&self, _workspace_id: WorkspaceId, cx: &mut ViewContext<Self>) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
|
|
|
@ -36,6 +36,13 @@ impl Bind for &[u8] {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const C: usize> Bind for &[u8; C] {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||
statement.bind_blob(start_index, self.as_slice())?;
|
||||
Ok(start_index + 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Bind for Vec<u8> {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||
statement.bind_blob(start_index, self)?;
|
||||
|
|
|
@ -52,57 +52,3 @@ impl Connection {
|
|||
Ok(move |bindings| statement.with_bindings(bindings)?.maybe_row::<C>())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! exec_method {
|
||||
($id:ident(): $sql:literal) => {
|
||||
pub fn $id(&self) -> $crate::anyhow::Result<()> {
|
||||
iife!({
|
||||
self.exec($sql)?()
|
||||
})
|
||||
}
|
||||
};
|
||||
($id:ident($($arg:ident: $arg_type:ty),+): $sql:literal) => {
|
||||
pub fn $id(&self, $($arg: $arg_type),+) -> $crate::anyhow::Result<()> {
|
||||
iife!({
|
||||
self.exec_bound::<($($arg_type),+)>($sql)?(($($arg),+))
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! select_method {
|
||||
($id:ident() -> $return_type:ty: $sql:literal) => {
|
||||
pub fn $id(&self) -> $crate::anyhow::Result<Vec<$return_type>> {
|
||||
iife!({
|
||||
self.select::<$return_type>($sql)?(())
|
||||
})
|
||||
}
|
||||
};
|
||||
($id:ident($($arg:ident: $arg_type:ty),+) -> $return_type:ty: $sql:literal) => {
|
||||
pub fn $id(&self, $($arg: $arg_type),+) -> $crate::anyhow::Result<Vec<$return_type>> {
|
||||
iife!({
|
||||
self.exec_bound::<($($arg_type),+), $return_type>($sql)?(($($arg),+))
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! select_row_method {
|
||||
($id:ident() -> $return_type:ty: $sql:literal) => {
|
||||
pub fn $id(&self) -> $crate::anyhow::Result<Option<$return_type>> {
|
||||
iife!({
|
||||
self.select_row::<$return_type>($sql)?(())
|
||||
})
|
||||
}
|
||||
};
|
||||
($id:ident($($arg:ident: $arg_type:ty),+) -> $return_type:ty: $sql:literal) => {
|
||||
pub fn $id(&self, $($arg: $arg_type),+) -> $crate::anyhow::Result<Option<$return_type>> {
|
||||
iife!({
|
||||
self.select_row_bound::<($($arg_type),+), $return_type>($sql)?(($($arg),+))
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use db::{
|
||||
connection, indoc,
|
||||
sqlez::{domain::Domain, exec_method, select_row_method},
|
||||
};
|
||||
use util::iife;
|
||||
use db::{connection, exec_method, indoc, select_row_method, sqlez::domain::Domain};
|
||||
|
||||
use workspace::{ItemId, Workspace, WorkspaceId};
|
||||
|
||||
use crate::Terminal;
|
||||
|
@ -19,13 +16,12 @@ impl Domain for Terminal {
|
|||
fn migrations() -> &'static [&'static str] {
|
||||
&[indoc! {"
|
||||
CREATE TABLE terminals (
|
||||
item_id INTEGER,
|
||||
workspace_id BLOB,
|
||||
item_id INTEGER,
|
||||
working_directory BLOB,
|
||||
PRIMARY KEY(item_id, workspace_id),
|
||||
PRIMARY KEY(workspace_id, item_id),
|
||||
FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
) STRICT;
|
||||
"}]
|
||||
}
|
||||
|
@ -33,15 +29,15 @@ impl Domain for Terminal {
|
|||
|
||||
impl TerminalDb {
|
||||
exec_method!(
|
||||
save_working_directory(item_id: ItemId, workspace_id: &WorkspaceId, working_directory: &Path):
|
||||
save_working_directory(model_id: ItemId, workspace_id: WorkspaceId, working_directory: &Path):
|
||||
"INSERT OR REPLACE INTO terminals(item_id, workspace_id, working_directory)
|
||||
VALUES (?, ?, ?)"
|
||||
VALUES (?1, ?2, ?3)"
|
||||
);
|
||||
|
||||
select_row_method!(
|
||||
get_working_directory(item_id: ItemId, workspace_id: &WorkspaceId) -> PathBuf:
|
||||
get_working_directory(item_id: ItemId, workspace_id: WorkspaceId) -> PathBuf:
|
||||
"SELECT working_directory
|
||||
FROM terminals
|
||||
WHERE item_id = ? workspace_id = ?"
|
||||
WHERE item_id = ? AND workspace_id = ?"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -33,9 +33,11 @@ use mappings::mouse::{
|
|||
alt_scroll, grid_point, mouse_button_report, mouse_moved_report, mouse_side, scroll_report,
|
||||
};
|
||||
|
||||
use persistence::TERMINAL_CONNECTION;
|
||||
use procinfo::LocalProcessInfo;
|
||||
use settings::{AlternateScroll, Settings, Shell, TerminalBlink};
|
||||
use util::ResultExt;
|
||||
use workspace::{ItemId, WorkspaceId};
|
||||
|
||||
use std::{
|
||||
cmp::min,
|
||||
|
@ -282,6 +284,8 @@ impl TerminalBuilder {
|
|||
blink_settings: Option<TerminalBlink>,
|
||||
alternate_scroll: &AlternateScroll,
|
||||
window_id: usize,
|
||||
item_id: ItemId,
|
||||
workspace_id: WorkspaceId,
|
||||
) -> Result<TerminalBuilder> {
|
||||
let pty_config = {
|
||||
let alac_shell = shell.clone().and_then(|shell| match shell {
|
||||
|
@ -386,6 +390,8 @@ impl TerminalBuilder {
|
|||
last_mouse_position: None,
|
||||
next_link_id: 0,
|
||||
selection_phase: SelectionPhase::Ended,
|
||||
workspace_id,
|
||||
item_id,
|
||||
};
|
||||
|
||||
Ok(TerminalBuilder {
|
||||
|
@ -529,6 +535,8 @@ pub struct Terminal {
|
|||
scroll_px: f32,
|
||||
next_link_id: usize,
|
||||
selection_phase: SelectionPhase,
|
||||
workspace_id: WorkspaceId,
|
||||
item_id: ItemId,
|
||||
}
|
||||
|
||||
impl Terminal {
|
||||
|
@ -566,20 +574,6 @@ impl Terminal {
|
|||
}
|
||||
AlacTermEvent::Wakeup => {
|
||||
cx.emit(Event::Wakeup);
|
||||
|
||||
if self.update_process_info() {
|
||||
cx.emit(Event::TitleChanged);
|
||||
|
||||
// if let Some(foreground_info) = self.foreground_process_info {
|
||||
// cx.background().spawn(async move {
|
||||
// TERMINAL_CONNECTION.save_working_directory(
|
||||
// self.item_id,
|
||||
// &self.workspace_id,
|
||||
// &foreground_info.cwd,
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
}
|
||||
}
|
||||
AlacTermEvent::ColorRequest(idx, fun_ptr) => {
|
||||
self.events
|
||||
|
@ -888,6 +882,19 @@ impl Terminal {
|
|||
|
||||
if self.update_process_info() {
|
||||
cx.emit(Event::TitleChanged);
|
||||
|
||||
if let Some(foreground_info) = &self.foreground_process_info {
|
||||
let cwd = foreground_info.cwd.clone();
|
||||
let item_id = self.item_id;
|
||||
let workspace_id = self.workspace_id;
|
||||
cx.background()
|
||||
.spawn(async move {
|
||||
TERMINAL_CONNECTION
|
||||
.save_working_directory(item_id, workspace_id, cwd.as_path())
|
||||
.log_err();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
//Note that the ordering of events matters for event processing
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::persistence::TERMINAL_CONNECTION;
|
||||
use crate::terminal_view::TerminalView;
|
||||
use crate::{Event, Terminal, TerminalBuilder, TerminalError};
|
||||
use crate::{Event, TerminalBuilder, TerminalError};
|
||||
|
||||
use alacritty_terminal::index::Point;
|
||||
use dirs::home_dir;
|
||||
|
@ -14,7 +14,7 @@ use workspace::{
|
|||
item::{Item, ItemEvent},
|
||||
ToolbarItemLocation, Workspace,
|
||||
};
|
||||
use workspace::{register_deserializable_item, Pane};
|
||||
use workspace::{register_deserializable_item, Pane, WorkspaceId};
|
||||
|
||||
use project::{LocalWorktree, Project, ProjectPath};
|
||||
use settings::{AlternateScroll, Settings, WorkingDirectory};
|
||||
|
@ -82,7 +82,9 @@ impl TerminalContainer {
|
|||
.unwrap_or(WorkingDirectory::CurrentProjectDirectory);
|
||||
|
||||
let working_directory = get_working_directory(workspace, cx, strategy);
|
||||
let view = cx.add_view(|cx| TerminalContainer::new(working_directory, false, cx));
|
||||
let view = cx.add_view(|cx| {
|
||||
TerminalContainer::new(working_directory, false, workspace.database_id(), cx)
|
||||
});
|
||||
workspace.add_item(Box::new(view), cx);
|
||||
}
|
||||
|
||||
|
@ -90,6 +92,7 @@ impl TerminalContainer {
|
|||
pub fn new(
|
||||
working_directory: Option<PathBuf>,
|
||||
modal: bool,
|
||||
workspace_id: WorkspaceId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let settings = cx.global::<Settings>();
|
||||
|
@ -116,10 +119,13 @@ impl TerminalContainer {
|
|||
settings.terminal_overrides.blinking.clone(),
|
||||
scroll,
|
||||
cx.window_id(),
|
||||
cx.view_id(),
|
||||
workspace_id,
|
||||
) {
|
||||
Ok(terminal) => {
|
||||
let terminal = cx.add_model(|cx| terminal.subscribe(cx));
|
||||
let view = cx.add_view(|cx| TerminalView::from_terminal(terminal, modal, cx));
|
||||
|
||||
cx.subscribe(&view, |_this, _content, event, cx| cx.emit(*event))
|
||||
.detach();
|
||||
TerminalContainerContent::Connected(view)
|
||||
|
@ -139,18 +145,6 @@ impl TerminalContainer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_terminal(
|
||||
terminal: ModelHandle<Terminal>,
|
||||
modal: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let connected_view = cx.add_view(|cx| TerminalView::from_terminal(terminal, modal, cx));
|
||||
TerminalContainer {
|
||||
content: TerminalContainerContent::Connected(connected_view),
|
||||
associated_directory: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn connected(&self) -> Option<ViewHandle<TerminalView>> {
|
||||
match &self.content {
|
||||
TerminalContainerContent::Connected(vh) => Some(vh.clone()),
|
||||
|
@ -278,13 +272,18 @@ impl Item for TerminalContainer {
|
|||
.boxed()
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self> {
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
workspace_id: WorkspaceId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Self> {
|
||||
//From what I can tell, there's no way to tell the current working
|
||||
//Directory of the terminal from outside the shell. There might be
|
||||
//solutions to this, but they are non-trivial and require more IPC
|
||||
Some(TerminalContainer::new(
|
||||
self.associated_directory.clone(),
|
||||
false,
|
||||
workspace_id,
|
||||
cx,
|
||||
))
|
||||
}
|
||||
|
@ -391,9 +390,14 @@ impl Item for TerminalContainer {
|
|||
item_id: workspace::ItemId,
|
||||
cx: &mut ViewContext<Pane>,
|
||||
) -> Task<anyhow::Result<ViewHandle<Self>>> {
|
||||
let working_directory = TERMINAL_CONNECTION.get_working_directory(item_id, &workspace_id);
|
||||
let working_directory = TERMINAL_CONNECTION.get_working_directory(item_id, workspace_id);
|
||||
Task::ready(Ok(cx.add_view(|cx| {
|
||||
TerminalContainer::new(working_directory.log_err().flatten(), false, cx)
|
||||
TerminalContainer::new(
|
||||
working_directory.log_err().flatten(),
|
||||
false,
|
||||
workspace_id,
|
||||
cx,
|
||||
)
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -206,7 +206,7 @@ impl Dock {
|
|||
cx.focus(last_active_center_pane);
|
||||
}
|
||||
cx.emit(crate::Event::DockAnchorChanged);
|
||||
workspace.serialize_workspace(None, cx);
|
||||
workspace.serialize_workspace(cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,8 @@ use theme::Theme;
|
|||
use util::ResultExt;
|
||||
|
||||
use crate::{
|
||||
pane,
|
||||
persistence::model::{ItemId, WorkspaceId},
|
||||
searchable::SearchableItemHandle,
|
||||
DelayedDebouncedEditAction, FollowableItemBuilders, ItemNavHistory, Pane, ToolbarItemLocation,
|
||||
Workspace,
|
||||
pane, persistence::model::ItemId, searchable::SearchableItemHandle, DelayedDebouncedEditAction,
|
||||
FollowableItemBuilders, ItemNavHistory, Pane, ToolbarItemLocation, Workspace, WorkspaceId,
|
||||
};
|
||||
|
||||
#[derive(Eq, PartialEq, Hash)]
|
||||
|
@ -52,7 +49,7 @@ pub trait Item: View {
|
|||
fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]>;
|
||||
fn is_singleton(&self, cx: &AppContext) -> bool;
|
||||
fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>);
|
||||
fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
|
||||
fn clone_on_split(&self, _workspace_id: WorkspaceId, _: &mut ViewContext<Self>) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
|
@ -121,7 +118,9 @@ pub trait Item: View {
|
|||
fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option<Vec<ElementBox>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn serialized_item_kind() -> Option<&'static str>;
|
||||
|
||||
fn deserialize(
|
||||
project: ModelHandle<Project>,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
|
@ -144,7 +143,11 @@ pub trait ItemHandle: 'static + fmt::Debug {
|
|||
fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]>;
|
||||
fn is_singleton(&self, cx: &AppContext) -> bool;
|
||||
fn boxed_clone(&self) -> Box<dyn ItemHandle>;
|
||||
fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemHandle>>;
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
workspace_id: WorkspaceId,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Option<Box<dyn ItemHandle>>;
|
||||
fn added_to_pane(
|
||||
&self,
|
||||
workspace: &mut Workspace,
|
||||
|
@ -246,9 +249,13 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
|
|||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemHandle>> {
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
workspace_id: WorkspaceId,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Option<Box<dyn ItemHandle>> {
|
||||
self.update(cx, |item, cx| {
|
||||
cx.add_option_view(|cx| item.clone_on_split(cx))
|
||||
cx.add_option_view(|cx| item.clone_on_split(workspace_id, cx))
|
||||
})
|
||||
.map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
|
||||
}
|
||||
|
@ -812,7 +819,11 @@ pub(crate) mod test {
|
|||
self.push_to_nav_history(cx);
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
_workspace_id: WorkspaceId,
|
||||
_: &mut ViewContext<Self>,
|
||||
) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
|
|
|
@ -2,39 +2,38 @@
|
|||
|
||||
pub mod model;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{anyhow, bail, Result, Context};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use db::connection;
|
||||
use gpui::Axis;
|
||||
use indoc::indoc;
|
||||
|
||||
|
||||
use db::sqlez::domain::Domain;
|
||||
use util::{iife, unzip_option, ResultExt};
|
||||
|
||||
use crate::dock::DockPosition;
|
||||
use crate::WorkspaceId;
|
||||
|
||||
use super::Workspace;
|
||||
|
||||
use model::{
|
||||
GroupId, PaneId, SerializedItem, SerializedPane, SerializedPaneGroup,
|
||||
SerializedWorkspace, WorkspaceId,
|
||||
GroupId, PaneId, SerializedItem, SerializedPane, SerializedPaneGroup, SerializedWorkspace,
|
||||
WorkspaceLocation,
|
||||
};
|
||||
|
||||
connection!(DB: WorkspaceDb<Workspace>);
|
||||
|
||||
|
||||
impl Domain for Workspace {
|
||||
fn name() -> &'static str {
|
||||
"workspace"
|
||||
}
|
||||
|
||||
|
||||
fn migrations() -> &'static [&'static str] {
|
||||
&[indoc! {"
|
||||
CREATE TABLE workspaces(
|
||||
workspace_id BLOB PRIMARY KEY,
|
||||
workspace_location BLOB NOT NULL 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
|
||||
|
@ -97,21 +96,25 @@ impl WorkspaceDb {
|
|||
&self,
|
||||
worktree_roots: &[P],
|
||||
) -> Option<SerializedWorkspace> {
|
||||
let workspace_id: WorkspaceId = worktree_roots.into();
|
||||
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, dock_position): (WorkspaceId, DockPosition) = iife!({
|
||||
let (workspace_id, workspace_location, dock_position): (
|
||||
WorkspaceId,
|
||||
WorkspaceLocation,
|
||||
DockPosition,
|
||||
) = iife!({
|
||||
if worktree_roots.len() == 0 {
|
||||
self.select_row(indoc! {"
|
||||
SELECT workspace_id, dock_visible, dock_anchor
|
||||
SELECT workspace_id, workspace_location, dock_visible, dock_anchor
|
||||
FROM workspaces
|
||||
ORDER BY timestamp DESC LIMIT 1"})?()?
|
||||
} else {
|
||||
self.select_row_bound(indoc! {"
|
||||
SELECT workspace_id, dock_visible, dock_anchor
|
||||
SELECT workspace_id, workspace_location, dock_visible, dock_anchor
|
||||
FROM workspaces
|
||||
WHERE workspace_id = ?"})?(&workspace_id)?
|
||||
WHERE workspace_location = ?"})?(&workspace_location)?
|
||||
}
|
||||
.context("No workspaces found")
|
||||
})
|
||||
|
@ -119,13 +122,14 @@ impl WorkspaceDb {
|
|||
.flatten()?;
|
||||
|
||||
Some(SerializedWorkspace {
|
||||
workspace_id: workspace_id.clone(),
|
||||
id: workspace_id,
|
||||
location: workspace_location.clone(),
|
||||
dock_pane: self
|
||||
.get_dock_pane(&workspace_id)
|
||||
.get_dock_pane(workspace_id)
|
||||
.context("Getting dock pane")
|
||||
.log_err()?,
|
||||
center_group: self
|
||||
.get_center_pane_group(&workspace_id)
|
||||
.get_center_pane_group(workspace_id)
|
||||
.context("Getting center group")
|
||||
.log_err()?,
|
||||
dock_position,
|
||||
|
@ -134,72 +138,61 @@ impl WorkspaceDb {
|
|||
|
||||
/// Saves a workspace using the worktree roots. Will garbage collect any workspaces
|
||||
/// that used this workspace previously
|
||||
pub fn save_workspace(
|
||||
&self,
|
||||
old_id: Option<WorkspaceId>,
|
||||
workspace: &SerializedWorkspace,
|
||||
) {
|
||||
pub fn save_workspace(&self, workspace: &SerializedWorkspace) {
|
||||
self.with_savepoint("update_worktrees", || {
|
||||
// Clear out panes and pane_groups
|
||||
self.exec_bound(indoc! {"
|
||||
UPDATE workspaces SET dock_pane = NULL WHERE workspace_id = ?1;
|
||||
DELETE FROM pane_groups WHERE workspace_id = ?1;
|
||||
DELETE FROM panes WHERE workspace_id = ?1;"})?
|
||||
(old_id.as_ref().unwrap_or(&workspace.workspace_id)).context("Clearing old panes")?;
|
||||
|
||||
if let Some(old_id) = old_id {
|
||||
self.exec_bound(indoc! {"
|
||||
UPDATE OR REPLACE workspaces
|
||||
SET workspace_id = ?,
|
||||
dock_visible = ?,
|
||||
dock_anchor = ?,
|
||||
timestamp = CURRENT_TIMESTAMP
|
||||
WHERE workspace_id = ?"})?((
|
||||
&workspace.workspace_id,
|
||||
workspace.dock_position,
|
||||
&old_id,
|
||||
)).context("Updating workspace with new worktree roots")?;
|
||||
} else {
|
||||
self.exec_bound(
|
||||
"INSERT OR REPLACE INTO workspaces(workspace_id, dock_visible, dock_anchor) VALUES (?, ?, ?)",
|
||||
)?((&workspace.workspace_id, workspace.dock_position)).context("Uodating workspace")?;
|
||||
}
|
||||
|
||||
DELETE FROM panes WHERE workspace_id = ?1;"})?(workspace.id)
|
||||
.context("Clearing old panes")?;
|
||||
|
||||
// Update or insert
|
||||
self.exec_bound(indoc! {
|
||||
"INSERT OR REPLACE INTO
|
||||
workspaces(workspace_id, workspace_location, dock_visible, dock_anchor, timestamp)
|
||||
VALUES
|
||||
(?1, ?2, ?3, ?4, CURRENT_TIMESTAMP)"
|
||||
})?((workspace.id, &workspace.location, workspace.dock_position))
|
||||
.context("Updating workspace")?;
|
||||
|
||||
// Save center pane group and dock pane
|
||||
self.save_pane_group(&workspace.workspace_id, &workspace.center_group, None).context("save pane group in save workspace")?;
|
||||
|
||||
let dock_id = self.save_pane(&workspace.workspace_id, &workspace.dock_pane, None, true).context("save pane in save workspace")?;
|
||||
|
||||
self.save_pane_group(workspace.id, &workspace.center_group, None)
|
||||
.context("save pane group in save workspace")?;
|
||||
|
||||
let dock_id = self
|
||||
.save_pane(workspace.id, &workspace.dock_pane, None, true)
|
||||
.context("save pane in save workspace")?;
|
||||
|
||||
// Complete workspace initialization
|
||||
self.exec_bound(indoc! {"
|
||||
UPDATE workspaces
|
||||
SET dock_pane = ?
|
||||
WHERE workspace_id = ?"})?((
|
||||
dock_id,
|
||||
&workspace.workspace_id,
|
||||
)).context("Finishing initialization with dock pane")?;
|
||||
WHERE workspace_id = ?"})?((dock_id, workspace.id))
|
||||
.context("Finishing initialization with dock pane")?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Update workspace with roots {:?} failed.",
|
||||
workspace.workspace_id.paths()
|
||||
"Update workspace with roots {:?} and id {:?} failed.",
|
||||
workspace.location.paths(),
|
||||
workspace.id
|
||||
)
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
||||
/// Returns the previous workspace ids sorted by last modified along with their opened worktree roots
|
||||
pub fn recent_workspaces(&self, limit: usize) -> Vec<Arc<Vec<PathBuf>>> {
|
||||
pub fn recent_workspaces(&self, limit: usize) -> Vec<(WorkspaceId, WorkspaceLocation)> {
|
||||
iife!({
|
||||
// TODO, upgrade anyhow: https://docs.rs/anyhow/1.0.66/anyhow/fn.Ok.html
|
||||
Ok::<_, anyhow::Error>(
|
||||
self.select_bound::<usize, WorkspaceId>(
|
||||
"SELECT workspace_id FROM workspaces ORDER BY timestamp DESC LIMIT ?",
|
||||
self.select_bound::<usize, (WorkspaceId, WorkspaceLocation)>(
|
||||
"SELECT workspace_id, workspace_location FROM workspaces ORDER BY timestamp DESC LIMIT ?",
|
||||
)?(limit)?
|
||||
.into_iter()
|
||||
.map(|id| id.paths())
|
||||
.collect::<Vec<Arc<Vec<PathBuf>>>>(),
|
||||
.collect::<Vec<(WorkspaceId, WorkspaceLocation)>>(),
|
||||
)
|
||||
})
|
||||
.log_err()
|
||||
|
@ -208,7 +201,7 @@ impl WorkspaceDb {
|
|||
|
||||
pub(crate) fn get_center_pane_group(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
workspace_id: WorkspaceId,
|
||||
) -> Result<SerializedPaneGroup> {
|
||||
self.get_pane_group(workspace_id, None)?
|
||||
.into_iter()
|
||||
|
@ -218,10 +211,10 @@ impl WorkspaceDb {
|
|||
|
||||
fn get_pane_group(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
workspace_id: WorkspaceId,
|
||||
group_id: Option<GroupId>,
|
||||
) -> Result<Vec<SerializedPaneGroup>> {
|
||||
type GroupKey<'a> = (Option<GroupId>, &'a WorkspaceId);
|
||||
type GroupKey = (Option<GroupId>, WorkspaceId);
|
||||
type GroupOrPane = (Option<GroupId>, Option<Axis>, Option<PaneId>, Option<bool>);
|
||||
self.select_bound::<GroupKey, GroupOrPane>(indoc! {"
|
||||
SELECT group_id, axis, pane_id, active
|
||||
|
@ -253,31 +246,29 @@ impl WorkspaceDb {
|
|||
if let Some((group_id, axis)) = group_id.zip(axis) {
|
||||
Ok(SerializedPaneGroup::Group {
|
||||
axis,
|
||||
children: self.get_pane_group(
|
||||
workspace_id,
|
||||
Some(group_id),
|
||||
)?,
|
||||
children: self.get_pane_group(workspace_id, Some(group_id))?,
|
||||
})
|
||||
} else if let Some((pane_id, active)) = pane_id.zip(active) {
|
||||
Ok(SerializedPaneGroup::Pane(SerializedPane::new(self.get_items( pane_id)?, active)))
|
||||
Ok(SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
self.get_items(pane_id)?,
|
||||
active,
|
||||
)))
|
||||
} else {
|
||||
bail!("Pane Group Child was neither a pane group or a pane");
|
||||
}
|
||||
})
|
||||
// Filter out panes and pane groups which don't have any children or items
|
||||
.filter(|pane_group| {
|
||||
match pane_group {
|
||||
Ok(SerializedPaneGroup::Group { children, .. }) => !children.is_empty(),
|
||||
Ok(SerializedPaneGroup::Pane(pane)) => !pane.children.is_empty(),
|
||||
_ => true,
|
||||
}
|
||||
.filter(|pane_group| match pane_group {
|
||||
Ok(SerializedPaneGroup::Group { children, .. }) => !children.is_empty(),
|
||||
Ok(SerializedPaneGroup::Pane(pane)) => !pane.children.is_empty(),
|
||||
_ => true,
|
||||
})
|
||||
.collect::<Result<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn save_pane_group(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
workspace_id: WorkspaceId,
|
||||
pane_group: &SerializedPaneGroup,
|
||||
parent: Option<(GroupId, usize)>,
|
||||
) -> Result<()> {
|
||||
|
@ -285,26 +276,31 @@ impl WorkspaceDb {
|
|||
SerializedPaneGroup::Group { axis, children } => {
|
||||
let (parent_id, position) = unzip_option(parent);
|
||||
|
||||
let group_id = self.select_row_bound::<_, i64>(indoc!{"
|
||||
let group_id = self.select_row_bound::<_, i64>(indoc! {"
|
||||
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"))?;
|
||||
|
||||
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(workspace_id, group, Some((group_id, position)))?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
SerializedPaneGroup::Pane(pane) => {
|
||||
self.save_pane(workspace_id, &pane, parent, false)?;
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_dock_pane(&self, workspace_id: &WorkspaceId) -> Result<SerializedPane> {
|
||||
pub(crate) fn get_dock_pane(&self, workspace_id: WorkspaceId) -> Result<SerializedPane> {
|
||||
let (pane_id, active) = self.select_row_bound(indoc! {"
|
||||
SELECT pane_id, active
|
||||
FROM panes
|
||||
|
@ -315,40 +311,35 @@ impl WorkspaceDb {
|
|||
|
||||
Ok(SerializedPane::new(
|
||||
self.get_items(pane_id).context("Reading items")?,
|
||||
active
|
||||
active,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn save_pane(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
workspace_id: WorkspaceId,
|
||||
pane: &SerializedPane,
|
||||
parent: Option<(GroupId, usize)>, // None indicates BOTH dock pane AND center_pane
|
||||
dock: bool,
|
||||
) -> Result<PaneId> {
|
||||
let pane_id = self.select_row_bound::<_, i64>(indoc!{"
|
||||
let pane_id = self.select_row_bound::<_, i64>(indoc! {"
|
||||
INSERT INTO panes(workspace_id, active)
|
||||
VALUES (?, ?)
|
||||
RETURNING pane_id"},
|
||||
)?((workspace_id, pane.active))?
|
||||
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);
|
||||
self.exec_bound(indoc! {"
|
||||
INSERT INTO center_panes(pane_id, parent_group_id, position)
|
||||
VALUES (?, ?, ?)"})?((
|
||||
pane_id, parent_id, order
|
||||
))?;
|
||||
VALUES (?, ?, ?)"})?((pane_id, parent_id, order))?;
|
||||
}
|
||||
|
||||
self.save_items(workspace_id, pane_id, &pane.children)
|
||||
.context("Saving items")?;
|
||||
|
||||
|
||||
Ok(pane_id)
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub(crate) fn get_items(&self, pane_id: PaneId) -> Result<Vec<SerializedItem>> {
|
||||
Ok(self.select_bound(indoc! {"
|
||||
|
@ -359,7 +350,7 @@ impl WorkspaceDb {
|
|||
|
||||
pub(crate) fn save_items(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
workspace_id: WorkspaceId,
|
||||
pane_id: PaneId,
|
||||
items: &[SerializedItem],
|
||||
) -> Result<()> {
|
||||
|
@ -376,7 +367,8 @@ impl WorkspaceDb {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use db::{open_memory_db};
|
||||
|
||||
use db::{open_memory_db, Uuid};
|
||||
use settings::DockAnchor;
|
||||
|
||||
use super::*;
|
||||
|
@ -388,15 +380,13 @@ mod tests {
|
|||
let db = WorkspaceDb(open_memory_db(Some("test_full_workspace_serialization")));
|
||||
|
||||
let dock_pane = crate::persistence::model::SerializedPane {
|
||||
|
||||
children: vec![
|
||||
SerializedItem::new("Terminal", 1),
|
||||
SerializedItem::new("Terminal", 2),
|
||||
SerializedItem::new("Terminal", 3),
|
||||
SerializedItem::new("Terminal", 4),
|
||||
|
||||
],
|
||||
active: false
|
||||
active: false,
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
@ -415,8 +405,8 @@ mod tests {
|
|||
SerializedItem::new("Terminal", 5),
|
||||
SerializedItem::new("Terminal", 6),
|
||||
],
|
||||
false)
|
||||
),
|
||||
false,
|
||||
)),
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
SerializedItem::new("Terminal", 7),
|
||||
|
@ -430,7 +420,6 @@ mod tests {
|
|||
vec![
|
||||
SerializedItem::new("Terminal", 9),
|
||||
SerializedItem::new("Terminal", 10),
|
||||
|
||||
],
|
||||
false,
|
||||
)),
|
||||
|
@ -438,25 +427,24 @@ mod tests {
|
|||
};
|
||||
|
||||
let workspace = SerializedWorkspace {
|
||||
workspace_id: (["/tmp", "/tmp2"]).into(),
|
||||
dock_position: DockPosition::Shown(DockAnchor::Bottom),
|
||||
id: Uuid::new(),
|
||||
location: (["/tmp", "/tmp2"]).into(),
|
||||
dock_position: DockPosition::Shown(DockAnchor::Bottom),
|
||||
center_group,
|
||||
dock_pane,
|
||||
};
|
||||
|
||||
db.save_workspace(None, &workspace);
|
||||
|
||||
db.save_workspace(&workspace);
|
||||
let round_trip_workspace = db.workspace_for_roots(&["/tmp2", "/tmp"]);
|
||||
|
||||
|
||||
assert_eq!(workspace, round_trip_workspace.unwrap());
|
||||
|
||||
// Test guaranteed duplicate IDs
|
||||
db.save_workspace(None, &workspace);
|
||||
db.save_workspace(None, &workspace);
|
||||
|
||||
db.save_workspace(&workspace);
|
||||
db.save_workspace(&workspace);
|
||||
|
||||
let round_trip_workspace = db.workspace_for_roots(&["/tmp", "/tmp2"]);
|
||||
assert_eq!(workspace, round_trip_workspace.unwrap());
|
||||
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -466,21 +454,23 @@ mod tests {
|
|||
let db = WorkspaceDb(open_memory_db(Some("test_basic_functionality")));
|
||||
|
||||
let workspace_1 = SerializedWorkspace {
|
||||
workspace_id: (["/tmp", "/tmp2"]).into(),
|
||||
id: WorkspaceId::new(),
|
||||
location: (["/tmp", "/tmp2"]).into(),
|
||||
dock_position: crate::dock::DockPosition::Shown(DockAnchor::Bottom),
|
||||
center_group: Default::default(),
|
||||
dock_pane: Default::default(),
|
||||
};
|
||||
|
||||
let mut workspace_2 = SerializedWorkspace {
|
||||
workspace_id: (["/tmp"]).into(),
|
||||
id: WorkspaceId::new(),
|
||||
location: (["/tmp"]).into(),
|
||||
dock_position: crate::dock::DockPosition::Hidden(DockAnchor::Expanded),
|
||||
center_group: Default::default(),
|
||||
dock_pane: Default::default(),
|
||||
};
|
||||
|
||||
db.save_workspace(None, &workspace_1);
|
||||
db.save_workspace(None, &workspace_2);
|
||||
db.save_workspace(&workspace_1);
|
||||
db.save_workspace(&workspace_2);
|
||||
|
||||
// Test that paths are treated as a set
|
||||
assert_eq!(
|
||||
|
@ -497,8 +487,9 @@ mod tests {
|
|||
assert_eq!(db.workspace_for_roots(&["/tmp3", "/tmp2", "/tmp4"]), None);
|
||||
|
||||
// Test 'mutate' case of updating a pre-existing id
|
||||
workspace_2.workspace_id = (["/tmp", "/tmp2"]).into();
|
||||
db.save_workspace(Some((&["/tmp"]).into()), &workspace_2);
|
||||
workspace_2.location = (["/tmp", "/tmp2"]).into();
|
||||
|
||||
db.save_workspace(&workspace_2);
|
||||
assert_eq!(
|
||||
db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(),
|
||||
workspace_2
|
||||
|
@ -506,33 +497,28 @@ mod tests {
|
|||
|
||||
// Test other mechanism for mutating
|
||||
let mut workspace_3 = SerializedWorkspace {
|
||||
workspace_id: (&["/tmp", "/tmp2"]).into(),
|
||||
id: WorkspaceId::new(),
|
||||
location: (&["/tmp", "/tmp2"]).into(),
|
||||
dock_position: DockPosition::Shown(DockAnchor::Right),
|
||||
center_group: Default::default(),
|
||||
dock_pane: Default::default(),
|
||||
};
|
||||
|
||||
|
||||
db.save_workspace(None, &workspace_3);
|
||||
db.save_workspace(&workspace_3);
|
||||
assert_eq!(
|
||||
db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(),
|
||||
workspace_3
|
||||
);
|
||||
|
||||
// Make sure that updating paths differently also works
|
||||
workspace_3.workspace_id = (["/tmp3", "/tmp4", "/tmp2"]).into();
|
||||
db.save_workspace(
|
||||
Some((&["/tmp", "/tmp2"]).into()),
|
||||
&workspace_3,
|
||||
);
|
||||
workspace_3.location = (["/tmp3", "/tmp4", "/tmp2"]).into();
|
||||
db.save_workspace(&workspace_3);
|
||||
assert_eq!(db.workspace_for_roots(&["/tmp2", "tmp"]), None);
|
||||
assert_eq!(
|
||||
db.workspace_for_roots(&["/tmp2", "/tmp3", "/tmp4"])
|
||||
.unwrap(),
|
||||
workspace_3
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
use crate::dock::DockPosition;
|
||||
|
@ -545,7 +531,8 @@ mod tests {
|
|||
center_group: &SerializedPaneGroup,
|
||||
) -> SerializedWorkspace {
|
||||
SerializedWorkspace {
|
||||
workspace_id: workspace_id.into(),
|
||||
id: WorkspaceId::new(),
|
||||
location: workspace_id.into(),
|
||||
dock_position: crate::dock::DockPosition::Hidden(DockAnchor::Right),
|
||||
center_group: center_group.clone(),
|
||||
dock_pane,
|
||||
|
@ -564,12 +551,13 @@ mod tests {
|
|||
SerializedItem::new("Terminal", 4),
|
||||
SerializedItem::new("Terminal", 2),
|
||||
SerializedItem::new("Terminal", 3),
|
||||
], false
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
let workspace = default_workspace(&["/tmp"], dock_pane, &Default::default());
|
||||
|
||||
db.save_workspace(None, &workspace);
|
||||
db.save_workspace(&workspace);
|
||||
|
||||
let new_workspace = db.workspace_for_roots(&["/tmp"]).unwrap();
|
||||
|
||||
|
@ -593,16 +581,20 @@ mod tests {
|
|||
SerializedPaneGroup::Group {
|
||||
axis: gpui::Axis::Vertical,
|
||||
children: vec![
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
SerializedItem::new("Terminal", 1),
|
||||
SerializedItem::new("Terminal", 2),
|
||||
],
|
||||
false)),
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(vec![
|
||||
SerializedItem::new("Terminal", 4),
|
||||
SerializedItem::new("Terminal", 3),
|
||||
], true)),
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
SerializedItem::new("Terminal", 1),
|
||||
SerializedItem::new("Terminal", 2),
|
||||
],
|
||||
false,
|
||||
)),
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
SerializedItem::new("Terminal", 4),
|
||||
SerializedItem::new("Terminal", 3),
|
||||
],
|
||||
true,
|
||||
)),
|
||||
],
|
||||
},
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
|
@ -610,41 +602,46 @@ mod tests {
|
|||
SerializedItem::new("Terminal", 5),
|
||||
SerializedItem::new("Terminal", 6),
|
||||
],
|
||||
false)),
|
||||
false,
|
||||
)),
|
||||
],
|
||||
};
|
||||
|
||||
let workspace = default_workspace(&["/tmp"], Default::default(), ¢er_pane);
|
||||
|
||||
db.save_workspace(None, &workspace);
|
||||
|
||||
db.save_workspace(&workspace);
|
||||
|
||||
let new_workspace = db.workspace_for_roots(&["/tmp"]).unwrap();
|
||||
|
||||
assert_eq!(workspace.center_group, new_workspace.center_group);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_cleanup_panes() {
|
||||
env_logger::try_init().ok();
|
||||
|
||||
|
||||
let db = WorkspaceDb(open_memory_db(Some("test_cleanup_panes")));
|
||||
|
||||
|
||||
let center_pane = SerializedPaneGroup::Group {
|
||||
axis: gpui::Axis::Horizontal,
|
||||
children: vec![
|
||||
SerializedPaneGroup::Group {
|
||||
axis: gpui::Axis::Vertical,
|
||||
children: vec![
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
SerializedItem::new("Terminal", 1),
|
||||
SerializedItem::new("Terminal", 2),
|
||||
],
|
||||
false)),
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(vec![
|
||||
SerializedItem::new("Terminal", 4),
|
||||
SerializedItem::new("Terminal", 3),
|
||||
], true)),
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
SerializedItem::new("Terminal", 1),
|
||||
SerializedItem::new("Terminal", 2),
|
||||
],
|
||||
false,
|
||||
)),
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
SerializedItem::new("Terminal", 4),
|
||||
SerializedItem::new("Terminal", 3),
|
||||
],
|
||||
true,
|
||||
)),
|
||||
],
|
||||
},
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
|
@ -652,37 +649,41 @@ mod tests {
|
|||
SerializedItem::new("Terminal", 5),
|
||||
SerializedItem::new("Terminal", 6),
|
||||
],
|
||||
false)),
|
||||
false,
|
||||
)),
|
||||
],
|
||||
};
|
||||
|
||||
let id = &["/tmp"];
|
||||
|
||||
|
||||
let mut workspace = default_workspace(id, Default::default(), ¢er_pane);
|
||||
|
||||
db.save_workspace(None, &workspace);
|
||||
|
||||
db.save_workspace(&workspace);
|
||||
|
||||
workspace.center_group = SerializedPaneGroup::Group {
|
||||
axis: gpui::Axis::Vertical,
|
||||
children: vec![
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
SerializedItem::new("Terminal", 1),
|
||||
SerializedItem::new("Terminal", 2),
|
||||
],
|
||||
false)),
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(vec![
|
||||
SerializedItem::new("Terminal", 4),
|
||||
SerializedItem::new("Terminal", 3),
|
||||
], true)),
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
SerializedItem::new("Terminal", 1),
|
||||
SerializedItem::new("Terminal", 2),
|
||||
],
|
||||
false,
|
||||
)),
|
||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||
vec![
|
||||
SerializedItem::new("Terminal", 4),
|
||||
SerializedItem::new("Terminal", 3),
|
||||
],
|
||||
true,
|
||||
)),
|
||||
],
|
||||
};
|
||||
|
||||
db.save_workspace(None, &workspace);
|
||||
|
||||
|
||||
db.save_workspace(&workspace);
|
||||
|
||||
let new_workspace = db.workspace_for_roots(id).unwrap();
|
||||
|
||||
assert_eq!(workspace.center_group, new_workspace.center_group);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,20 @@ use project::Project;
|
|||
use settings::DockAnchor;
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::{dock::DockPosition, ItemDeserializers, Member, Pane, PaneAxis, Workspace};
|
||||
use crate::{
|
||||
dock::DockPosition, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct WorkspaceId(Arc<Vec<PathBuf>>);
|
||||
pub struct WorkspaceLocation(Arc<Vec<PathBuf>>);
|
||||
|
||||
impl WorkspaceId {
|
||||
impl WorkspaceLocation {
|
||||
pub fn paths(&self) -> Arc<Vec<PathBuf>> {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: AsRef<Path>, T: IntoIterator<Item = P>> From<T> for WorkspaceId {
|
||||
impl<P: AsRef<Path>, T: IntoIterator<Item = P>> From<T> for WorkspaceLocation {
|
||||
fn from(iterator: T) -> Self {
|
||||
let mut roots = iterator
|
||||
.into_iter()
|
||||
|
@ -38,7 +40,7 @@ impl<P: AsRef<Path>, T: IntoIterator<Item = P>> From<T> for WorkspaceId {
|
|||
}
|
||||
}
|
||||
|
||||
impl Bind for &WorkspaceId {
|
||||
impl Bind for &WorkspaceLocation {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||
bincode::serialize(&self.0)
|
||||
.expect("Bincode serialization of paths should not fail")
|
||||
|
@ -46,16 +48,20 @@ impl Bind for &WorkspaceId {
|
|||
}
|
||||
}
|
||||
|
||||
impl Column for WorkspaceId {
|
||||
impl Column for WorkspaceLocation {
|
||||
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
|
||||
let blob = statement.column_blob(start_index)?;
|
||||
Ok((WorkspaceId(bincode::deserialize(blob)?), start_index + 1))
|
||||
Ok((
|
||||
WorkspaceLocation(bincode::deserialize(blob)?),
|
||||
start_index + 1,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct SerializedWorkspace {
|
||||
pub workspace_id: WorkspaceId,
|
||||
pub id: WorkspaceId,
|
||||
pub location: WorkspaceLocation,
|
||||
pub dock_position: DockPosition,
|
||||
pub center_group: SerializedPaneGroup,
|
||||
pub dock_pane: SerializedPane,
|
||||
|
@ -70,10 +76,11 @@ pub enum SerializedPaneGroup {
|
|||
Pane(SerializedPane),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Default for SerializedPaneGroup {
|
||||
fn default() -> Self {
|
||||
Self::Pane(SerializedPane {
|
||||
children: Vec::new(),
|
||||
children: vec![SerializedItem::default()],
|
||||
active: false,
|
||||
})
|
||||
}
|
||||
|
@ -84,7 +91,7 @@ impl SerializedPaneGroup {
|
|||
pub(crate) async fn deserialize(
|
||||
&self,
|
||||
project: &ModelHandle<Project>,
|
||||
workspace_id: &WorkspaceId,
|
||||
workspace_id: WorkspaceId,
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> (Member, Option<ViewHandle<Pane>>) {
|
||||
|
@ -136,13 +143,12 @@ impl SerializedPane {
|
|||
&self,
|
||||
project: &ModelHandle<Project>,
|
||||
pane_handle: &ViewHandle<Pane>,
|
||||
workspace_id: &WorkspaceId,
|
||||
workspace_id: WorkspaceId,
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) {
|
||||
for item in self.children.iter() {
|
||||
let project = project.clone();
|
||||
let workspace_id = workspace_id.clone();
|
||||
let item_handle = pane_handle
|
||||
.update(cx, |_, cx| {
|
||||
if let Some(deserializer) = cx.global::<ItemDeserializers>().get(&item.kind) {
|
||||
|
@ -191,6 +197,16 @@ impl SerializedItem {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Default for SerializedItem {
|
||||
fn default() -> Self {
|
||||
SerializedItem {
|
||||
kind: Arc::from("Terminal"),
|
||||
item_id: 100000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bind for &SerializedItem {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||
let next_index = statement.bind(self.kind.clone(), start_index)?;
|
||||
|
@ -231,7 +247,7 @@ mod tests {
|
|||
use db::sqlez::connection::Connection;
|
||||
use settings::DockAnchor;
|
||||
|
||||
use super::WorkspaceId;
|
||||
use super::WorkspaceLocation;
|
||||
|
||||
#[test]
|
||||
fn test_workspace_round_trips() {
|
||||
|
@ -245,7 +261,7 @@ mod tests {
|
|||
.unwrap()()
|
||||
.unwrap();
|
||||
|
||||
let workspace_id: WorkspaceId = WorkspaceId::from(&["\test2", "\test1"]);
|
||||
let workspace_id: WorkspaceLocation = WorkspaceLocation::from(&["\test2", "\test1"]);
|
||||
|
||||
db.exec_bound("INSERT INTO workspace_id_test(workspace_id, dock_anchor) VALUES (?,?)")
|
||||
.unwrap()((&workspace_id, DockAnchor::Bottom))
|
||||
|
@ -255,7 +271,10 @@ mod tests {
|
|||
db.select_row("SELECT workspace_id, dock_anchor FROM workspace_id_test LIMIT 1")
|
||||
.unwrap()()
|
||||
.unwrap(),
|
||||
Some((WorkspaceId::from(&["\test1", "\test2"]), DockAnchor::Bottom))
|
||||
Some((
|
||||
WorkspaceLocation::from(&["\test1", "\test2"]),
|
||||
DockAnchor::Bottom
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use crate::{
|
||||
item::ItemEvent,
|
||||
persistence::model::{ItemId, WorkspaceId},
|
||||
Item, ItemNavHistory, Pane, Workspace,
|
||||
item::ItemEvent, persistence::model::ItemId, Item, ItemNavHistory, Pane, Workspace, WorkspaceId,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use call::participant::{Frame, RemoteVideoTrack};
|
||||
|
@ -148,7 +146,11 @@ impl Item for SharedScreen {
|
|||
self.nav_history = Some(history);
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self> {
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
_workspace_id: WorkspaceId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Self> {
|
||||
let track = self.track.upgrade()?;
|
||||
Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ use anyhow::{anyhow, Context, Result};
|
|||
use call::ActiveCall;
|
||||
use client::{proto, Client, PeerId, TypedEnvelope, UserStore};
|
||||
use collections::{hash_map, HashMap, HashSet};
|
||||
use db::Uuid;
|
||||
use dock::{DefaultItemFactory, Dock, ToggleDockButton};
|
||||
use drag_and_drop::DragAndDrop;
|
||||
use fs::{self, Fs};
|
||||
|
@ -45,7 +46,7 @@ use log::{error, warn};
|
|||
pub use pane::*;
|
||||
pub use pane_group::*;
|
||||
use persistence::model::SerializedItem;
|
||||
pub use persistence::model::{ItemId, WorkspaceId};
|
||||
pub use persistence::model::{ItemId, WorkspaceLocation};
|
||||
use postage::prelude::Stream;
|
||||
use project::{Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId};
|
||||
use serde::Deserialize;
|
||||
|
@ -128,6 +129,8 @@ pub struct OpenProjectEntryInPane {
|
|||
project_entry: ProjectEntryId,
|
||||
}
|
||||
|
||||
pub type WorkspaceId = Uuid;
|
||||
|
||||
impl_internal_actions!(
|
||||
workspace,
|
||||
[
|
||||
|
@ -530,6 +533,7 @@ pub struct Workspace {
|
|||
last_leaders_by_pane: HashMap<WeakViewHandle<Pane>, PeerId>,
|
||||
window_edited: bool,
|
||||
active_call: Option<(ModelHandle<ActiveCall>, Vec<gpui::Subscription>)>,
|
||||
database_id: WorkspaceId,
|
||||
_observe_current_user: Task<()>,
|
||||
}
|
||||
|
||||
|
@ -556,7 +560,7 @@ impl Workspace {
|
|||
project::Event::WorktreeRemoved(_) | project::Event::WorktreeAdded => {
|
||||
this.update_window_title(cx);
|
||||
// TODO: Cache workspace_id on workspace and read from it here
|
||||
this.serialize_workspace(None, cx);
|
||||
this.serialize_workspace(cx);
|
||||
}
|
||||
project::Event::DisconnectedFromHost => {
|
||||
this.update_window_edited(cx);
|
||||
|
@ -630,6 +634,12 @@ impl Workspace {
|
|||
active_call = Some((call, subscriptions));
|
||||
}
|
||||
|
||||
let id = if let Some(id) = serialized_workspace.as_ref().map(|ws| ws.id) {
|
||||
id
|
||||
} else {
|
||||
WorkspaceId::new()
|
||||
};
|
||||
|
||||
let mut this = Workspace {
|
||||
modal: None,
|
||||
weak_self: weak_handle.clone(),
|
||||
|
@ -657,6 +667,7 @@ impl Workspace {
|
|||
last_leaders_by_pane: Default::default(),
|
||||
window_edited: false,
|
||||
active_call,
|
||||
database_id: id,
|
||||
_observe_current_user,
|
||||
};
|
||||
this.project_remote_id_changed(project.read(cx).remote_id(), cx);
|
||||
|
@ -1317,7 +1328,7 @@ impl Workspace {
|
|||
pub fn add_item(&mut self, item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
|
||||
let active_pane = self.active_pane().clone();
|
||||
Pane::add_item(self, &active_pane, item, true, true, None, cx);
|
||||
self.serialize_workspace(None, cx);
|
||||
self.serialize_workspace(cx);
|
||||
}
|
||||
|
||||
pub fn open_path(
|
||||
|
@ -1522,7 +1533,7 @@ impl Workspace {
|
|||
entry.remove();
|
||||
}
|
||||
}
|
||||
self.serialize_workspace(None, cx);
|
||||
self.serialize_workspace(cx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -1544,7 +1555,7 @@ impl Workspace {
|
|||
|
||||
pane.read(cx).active_item().map(|item| {
|
||||
let new_pane = self.add_pane(cx);
|
||||
if let Some(clone) = item.clone_on_split(cx.as_mut()) {
|
||||
if let Some(clone) = item.clone_on_split(self.database_id(), cx.as_mut()) {
|
||||
Pane::add_item(self, &new_pane, clone, true, true, None, cx);
|
||||
}
|
||||
self.center.split(&pane, &new_pane, direction).unwrap();
|
||||
|
@ -2255,7 +2266,11 @@ impl Workspace {
|
|||
}
|
||||
}
|
||||
|
||||
fn workspace_id(&self, cx: &AppContext) -> WorkspaceId {
|
||||
pub fn database_id(&self) -> WorkspaceId {
|
||||
self.database_id
|
||||
}
|
||||
|
||||
fn location(&self, cx: &AppContext) -> WorkspaceLocation {
|
||||
self.project()
|
||||
.read(cx)
|
||||
.visible_worktrees(cx)
|
||||
|
@ -2275,7 +2290,7 @@ impl Workspace {
|
|||
}
|
||||
}
|
||||
|
||||
fn serialize_workspace(&self, old_id: Option<WorkspaceId>, cx: &AppContext) {
|
||||
fn serialize_workspace(&self, cx: &AppContext) {
|
||||
fn serialize_pane_handle(
|
||||
pane_handle: &ViewHandle<Pane>,
|
||||
cx: &AppContext,
|
||||
|
@ -2320,7 +2335,8 @@ impl Workspace {
|
|||
let center_group = build_serialized_pane_group(&self.center.root, cx);
|
||||
|
||||
let serialized_workspace = SerializedWorkspace {
|
||||
workspace_id: self.workspace_id(cx),
|
||||
id: self.database_id,
|
||||
location: self.location(cx),
|
||||
dock_position: self.dock.position(),
|
||||
dock_pane,
|
||||
center_group,
|
||||
|
@ -2328,7 +2344,7 @@ impl Workspace {
|
|||
|
||||
cx.background()
|
||||
.spawn(async move {
|
||||
persistence::DB.save_workspace(old_id, &serialized_workspace);
|
||||
persistence::DB.save_workspace(&serialized_workspace);
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
@ -2349,7 +2365,7 @@ impl Workspace {
|
|||
.deserialize_to(
|
||||
&project,
|
||||
&dock_pane_handle,
|
||||
&serialized_workspace.workspace_id,
|
||||
serialized_workspace.id,
|
||||
&workspace,
|
||||
&mut cx,
|
||||
)
|
||||
|
@ -2359,12 +2375,7 @@ impl Workspace {
|
|||
|
||||
let (root, active_pane) = serialized_workspace
|
||||
.center_group
|
||||
.deserialize(
|
||||
&project,
|
||||
&serialized_workspace.workspace_id,
|
||||
&workspace,
|
||||
&mut cx,
|
||||
)
|
||||
.deserialize(&project, serialized_workspace.id, &workspace, &mut cx)
|
||||
.await;
|
||||
|
||||
// Remove old panes from workspace panes list
|
||||
|
|
|
@ -597,6 +597,8 @@ pub fn default_item_factory(
|
|||
|
||||
let working_directory = get_working_directory(workspace, cx, strategy);
|
||||
|
||||
let terminal_handle = cx.add_view(|cx| TerminalContainer::new(working_directory, false, cx));
|
||||
let terminal_handle = cx.add_view(|cx| {
|
||||
TerminalContainer::new(working_directory, false, workspace.database_id(), cx)
|
||||
});
|
||||
Box::new(terminal_handle)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue