ssh remoting: Restore SSH projects when reopening Zed (#19188)
Release Notes: - N/A --------- Co-authored-by: Bennet <bennet@zed.dev>
This commit is contained in:
parent
71a878aa39
commit
6ec00cdb06
5 changed files with 248 additions and 70 deletions
|
@ -732,9 +732,11 @@ impl WorkspaceDb {
|
|||
bottom_dock_visible,
|
||||
bottom_dock_active_panel,
|
||||
bottom_dock_zoom,
|
||||
session_id,
|
||||
window_id,
|
||||
timestamp
|
||||
)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, CURRENT_TIMESTAMP)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, CURRENT_TIMESTAMP)
|
||||
ON CONFLICT DO
|
||||
UPDATE SET
|
||||
ssh_project_id = ?2,
|
||||
|
@ -747,11 +749,15 @@ impl WorkspaceDb {
|
|||
bottom_dock_visible = ?9,
|
||||
bottom_dock_active_panel = ?10,
|
||||
bottom_dock_zoom = ?11,
|
||||
session_id = ?12,
|
||||
window_id = ?13,
|
||||
timestamp = CURRENT_TIMESTAMP
|
||||
))?((
|
||||
workspace.id,
|
||||
ssh_project.id.0,
|
||||
workspace.docks,
|
||||
workspace.session_id,
|
||||
workspace.window_id
|
||||
))
|
||||
.context("Updating workspace")?;
|
||||
}
|
||||
|
@ -827,8 +833,8 @@ impl WorkspaceDb {
|
|||
}
|
||||
|
||||
query! {
|
||||
fn session_workspaces(session_id: String) -> Result<Vec<(LocalPaths, Option<u64>)>> {
|
||||
SELECT local_paths, window_id
|
||||
fn session_workspaces(session_id: String) -> Result<Vec<(LocalPaths, Option<u64>, Option<u64>)>> {
|
||||
SELECT local_paths, window_id, ssh_project_id
|
||||
FROM workspaces
|
||||
WHERE session_id = ?1 AND dev_server_project_id IS NULL
|
||||
ORDER BY timestamp DESC
|
||||
|
@ -849,6 +855,14 @@ impl WorkspaceDb {
|
|||
}
|
||||
}
|
||||
|
||||
query! {
|
||||
fn ssh_project(id: u64) -> Result<SerializedSshProject> {
|
||||
SELECT id, host, port, paths, user
|
||||
FROM ssh_projects
|
||||
WHERE id = ?
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn last_window(
|
||||
&self,
|
||||
) -> anyhow::Result<(Option<Uuid>, Option<SerializedWindowBounds>)> {
|
||||
|
@ -937,18 +951,13 @@ impl WorkspaceDb {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn last_workspace(&self) -> Result<Option<LocalPaths>> {
|
||||
pub async fn last_workspace(&self) -> Result<Option<SerializedWorkspaceLocation>> {
|
||||
Ok(self
|
||||
.recent_workspaces_on_disk()
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter_map(|(_, location)| match location {
|
||||
SerializedWorkspaceLocation::Local(local_paths, _) => Some(local_paths),
|
||||
// Do not automatically reopen Dev Server and SSH workspaces
|
||||
SerializedWorkspaceLocation::DevServer(_) => None,
|
||||
SerializedWorkspaceLocation::Ssh(_) => None,
|
||||
})
|
||||
.next())
|
||||
.next()
|
||||
.map(|(_, location)| location))
|
||||
}
|
||||
|
||||
// Returns the locations of the workspaces that were still opened when the last
|
||||
|
@ -959,13 +968,20 @@ impl WorkspaceDb {
|
|||
&self,
|
||||
last_session_id: &str,
|
||||
last_session_window_stack: Option<Vec<WindowId>>,
|
||||
) -> Result<Vec<LocalPaths>> {
|
||||
) -> Result<Vec<SerializedWorkspaceLocation>> {
|
||||
let mut workspaces = Vec::new();
|
||||
|
||||
for (location, window_id) in self.session_workspaces(last_session_id.to_owned())? {
|
||||
if location.paths().iter().all(|path| path.exists())
|
||||
for (location, window_id, ssh_project_id) in
|
||||
self.session_workspaces(last_session_id.to_owned())?
|
||||
{
|
||||
if let Some(ssh_project_id) = ssh_project_id {
|
||||
let location = SerializedWorkspaceLocation::Ssh(self.ssh_project(ssh_project_id)?);
|
||||
workspaces.push((location, window_id.map(WindowId::from)));
|
||||
} else if location.paths().iter().all(|path| path.exists())
|
||||
&& location.paths().iter().any(|path| path.is_dir())
|
||||
{
|
||||
let location =
|
||||
SerializedWorkspaceLocation::from_local_paths(location.paths().iter());
|
||||
workspaces.push((location, window_id.map(WindowId::from)));
|
||||
}
|
||||
}
|
||||
|
@ -1570,10 +1586,28 @@ mod tests {
|
|||
window_id: None,
|
||||
};
|
||||
|
||||
let ssh_project = db
|
||||
.get_or_create_ssh_project("my-host".to_string(), Some(1234), vec![], None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let workspace_5 = SerializedWorkspace {
|
||||
id: WorkspaceId(5),
|
||||
location: SerializedWorkspaceLocation::Ssh(ssh_project.clone()),
|
||||
center_group: Default::default(),
|
||||
window_bounds: Default::default(),
|
||||
display: Default::default(),
|
||||
docks: Default::default(),
|
||||
centered_layout: false,
|
||||
session_id: Some("session-id-2".to_owned()),
|
||||
window_id: Some(50),
|
||||
};
|
||||
|
||||
db.save_workspace(workspace_1.clone()).await;
|
||||
db.save_workspace(workspace_2.clone()).await;
|
||||
db.save_workspace(workspace_3.clone()).await;
|
||||
db.save_workspace(workspace_4.clone()).await;
|
||||
db.save_workspace(workspace_5.clone()).await;
|
||||
|
||||
let locations = db.session_workspaces("session-id-1".to_owned()).unwrap();
|
||||
assert_eq!(locations.len(), 2);
|
||||
|
@ -1583,9 +1617,13 @@ mod tests {
|
|||
assert_eq!(locations[1].1, Some(20));
|
||||
|
||||
let locations = db.session_workspaces("session-id-2".to_owned()).unwrap();
|
||||
assert_eq!(locations.len(), 1);
|
||||
assert_eq!(locations.len(), 2);
|
||||
assert_eq!(locations[0].0, LocalPaths::new(["/tmp3"]));
|
||||
assert_eq!(locations[0].1, Some(30));
|
||||
let empty_paths: Vec<&str> = Vec::new();
|
||||
assert_eq!(locations[1].0, LocalPaths::new(empty_paths.iter()));
|
||||
assert_eq!(locations[1].1, Some(50));
|
||||
assert_eq!(locations[1].2, Some(ssh_project.id.0));
|
||||
}
|
||||
|
||||
fn default_workspace<P: AsRef<Path>>(
|
||||
|
@ -1650,10 +1688,97 @@ mod tests {
|
|||
.last_session_workspace_locations("one-session", stack)
|
||||
.unwrap();
|
||||
assert_eq!(have.len(), 4);
|
||||
assert_eq!(have[0], LocalPaths::new([dir4.path().to_str().unwrap()]));
|
||||
assert_eq!(have[1], LocalPaths::new([dir3.path().to_str().unwrap()]));
|
||||
assert_eq!(have[2], LocalPaths::new([dir2.path().to_str().unwrap()]));
|
||||
assert_eq!(have[3], LocalPaths::new([dir1.path().to_str().unwrap()]));
|
||||
assert_eq!(
|
||||
have[0],
|
||||
SerializedWorkspaceLocation::from_local_paths(&[dir4.path().to_str().unwrap()])
|
||||
);
|
||||
assert_eq!(
|
||||
have[1],
|
||||
SerializedWorkspaceLocation::from_local_paths([dir3.path().to_str().unwrap()])
|
||||
);
|
||||
assert_eq!(
|
||||
have[2],
|
||||
SerializedWorkspaceLocation::from_local_paths([dir2.path().to_str().unwrap()])
|
||||
);
|
||||
assert_eq!(
|
||||
have[3],
|
||||
SerializedWorkspaceLocation::from_local_paths([dir1.path().to_str().unwrap()])
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_last_session_workspace_locations_ssh_projects() {
|
||||
let db = WorkspaceDb(
|
||||
open_test_db("test_serializing_workspaces_last_session_workspaces_ssh_projects").await,
|
||||
);
|
||||
|
||||
let ssh_projects = [
|
||||
("host-1", "my-user-1"),
|
||||
("host-2", "my-user-2"),
|
||||
("host-3", "my-user-3"),
|
||||
("host-4", "my-user-4"),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(host, user)| async {
|
||||
db.get_or_create_ssh_project(host.to_string(), None, vec![], Some(user.to_string()))
|
||||
.await
|
||||
.unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ssh_projects = futures::future::join_all(ssh_projects).await;
|
||||
|
||||
let workspaces = [
|
||||
(1, ssh_projects[0].clone(), 9),
|
||||
(2, ssh_projects[1].clone(), 5),
|
||||
(3, ssh_projects[2].clone(), 8),
|
||||
(4, ssh_projects[3].clone(), 2),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(id, ssh_project, window_id)| SerializedWorkspace {
|
||||
id: WorkspaceId(id),
|
||||
location: SerializedWorkspaceLocation::Ssh(ssh_project),
|
||||
center_group: Default::default(),
|
||||
window_bounds: Default::default(),
|
||||
display: Default::default(),
|
||||
docks: Default::default(),
|
||||
centered_layout: false,
|
||||
session_id: Some("one-session".to_owned()),
|
||||
window_id: Some(window_id),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for workspace in workspaces.iter() {
|
||||
db.save_workspace(workspace.clone()).await;
|
||||
}
|
||||
|
||||
let stack = Some(Vec::from([
|
||||
WindowId::from(2), // Top
|
||||
WindowId::from(8),
|
||||
WindowId::from(5),
|
||||
WindowId::from(9), // Bottom
|
||||
]));
|
||||
|
||||
let have = db
|
||||
.last_session_workspace_locations("one-session", stack)
|
||||
.unwrap();
|
||||
assert_eq!(have.len(), 4);
|
||||
assert_eq!(
|
||||
have[0],
|
||||
SerializedWorkspaceLocation::Ssh(ssh_projects[3].clone())
|
||||
);
|
||||
assert_eq!(
|
||||
have[1],
|
||||
SerializedWorkspaceLocation::Ssh(ssh_projects[2].clone())
|
||||
);
|
||||
assert_eq!(
|
||||
have[2],
|
||||
SerializedWorkspaceLocation::Ssh(ssh_projects[1].clone())
|
||||
);
|
||||
assert_eq!(
|
||||
have[3],
|
||||
SerializedWorkspaceLocation::Ssh(ssh_projects[0].clone())
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue