@@ -939,6 +939,26 @@ impl WorkspaceDb {
}
}
+ query! {
+ pub async fn update_ssh_project_paths_query(ssh_project_id: u64, paths: String) -> Result<Option<SerializedSshProject>> {
+ UPDATE ssh_projects
+ SET paths = ?2
+ WHERE id = ?1
+ RETURNING id, host, port, paths, user
+ }
+ }
+
+ pub(crate) async fn update_ssh_project_paths(
+ &self,
+ ssh_project_id: SshProjectId,
+ new_paths: Vec<String>,
+ ) -> Result<SerializedSshProject> {
+ let paths = serde_json::to_string(&new_paths)?;
+ self.update_ssh_project_paths_query(ssh_project_id.0, paths)
+ .await?
+ .context("failed to update ssh project paths")
+ }
+
query! {
pub async fn next_id() -> Result<WorkspaceId> {
INSERT INTO workspaces DEFAULT VALUES RETURNING workspace_id
@@ -2624,4 +2644,56 @@ mod tests {
assert_eq!(workspace.center_group, new_workspace.center_group);
}
+
+ #[gpui::test]
+ async fn test_update_ssh_project_paths() {
+ zlog::init_test();
+
+ let db = WorkspaceDb::open_test_db("test_update_ssh_project_paths").await;
+
+ let (host, port, initial_paths, user) = (
+ "example.com".to_string(),
+ Some(22_u16),
+ vec!["/home/user".to_string(), "/etc/nginx".to_string()],
+ Some("user".to_string()),
+ );
+
+ let project = db
+ .get_or_create_ssh_project(host.clone(), port, initial_paths.clone(), user.clone())
+ .await
+ .unwrap();
+
+ assert_eq!(project.host, host);
+ assert_eq!(project.paths, initial_paths);
+ assert_eq!(project.user, user);
+
+ let new_paths = vec![
+ "/home/user".to_string(),
+ "/etc/nginx".to_string(),
+ "/var/log".to_string(),
+ "/opt/app".to_string(),
+ ];
+
+ let updated_project = db
+ .update_ssh_project_paths(project.id, new_paths.clone())
+ .await
+ .unwrap();
+
+ assert_eq!(updated_project.id, project.id);
+ assert_eq!(updated_project.paths, new_paths);
+
+ let retrieved_project = db
+ .get_ssh_project(
+ host.clone(),
+ port,
+ serde_json::to_string(&new_paths).unwrap(),
+ user.clone(),
+ )
+ .await
+ .unwrap()
+ .unwrap();
+
+ assert_eq!(retrieved_project.id, project.id);
+ assert_eq!(retrieved_project.paths, new_paths);
+ }
}
@@ -1094,7 +1094,8 @@ pub struct Workspace {
_subscriptions: Vec<Subscription>,
_apply_leader_updates: Task<Result<()>>,
_observe_current_user: Task<Result<()>>,
- _schedule_serialize: Option<Task<()>>,
+ _schedule_serialize_workspace: Option<Task<()>>,
+ _schedule_serialize_ssh_paths: Option<Task<()>>,
pane_history_timestamp: Arc<AtomicUsize>,
bounds: Bounds<Pixels>,
pub centered_layout: bool,
@@ -1153,6 +1154,8 @@ impl Workspace {
project::Event::WorktreeRemoved(_) | project::Event::WorktreeAdded(_) => {
this.update_window_title(window, cx);
+ this.update_ssh_paths(cx);
+ this.serialize_ssh_paths(window, cx);
this.serialize_workspace(window, cx);
// This event could be triggered by `AddFolderToProject` or `RemoveFromProject`.
this.update_history(cx);
@@ -1420,7 +1423,8 @@ impl Workspace {
app_state,
_observe_current_user,
_apply_leader_updates,
- _schedule_serialize: None,
+ _schedule_serialize_workspace: None,
+ _schedule_serialize_ssh_paths: None,
leader_updates_tx,
_subscriptions: subscriptions,
pane_history_timestamp,
@@ -5077,6 +5081,46 @@ impl Workspace {
}
}
+ fn update_ssh_paths(&mut self, cx: &App) {
+ let project = self.project().read(cx);
+ if !project.is_local() {
+ let paths: Vec<String> = project
+ .visible_worktrees(cx)
+ .map(|worktree| worktree.read(cx).abs_path().to_string_lossy().to_string())
+ .collect();
+ if let Some(ssh_project) = &mut self.serialized_ssh_project {
+ ssh_project.paths = paths;
+ }
+ }
+ }
+
+ fn serialize_ssh_paths(&mut self, window: &mut Window, cx: &mut Context<Workspace>) {
+ if self._schedule_serialize_ssh_paths.is_none() {
+ self._schedule_serialize_ssh_paths =
+ Some(cx.spawn_in(window, async move |this, cx| {
+ cx.background_executor()
+ .timer(SERIALIZATION_THROTTLE_TIME)
+ .await;
+ this.update_in(cx, |this, window, cx| {
+ let task = if let Some(ssh_project) = &this.serialized_ssh_project {
+ let ssh_project_id = ssh_project.id;
+ let ssh_project_paths = ssh_project.paths.clone();
+ window.spawn(cx, async move |_| {
+ persistence::DB
+ .update_ssh_project_paths(ssh_project_id, ssh_project_paths)
+ .await
+ })
+ } else {
+ Task::ready(Err(anyhow::anyhow!("No SSH project to serialize")))
+ };
+ task.detach();
+ this._schedule_serialize_ssh_paths.take();
+ })
+ .log_err();
+ }));
+ }
+ }
+
fn remove_panes(&mut self, member: Member, window: &mut Window, cx: &mut Context<Workspace>) {
match member {
Member::Axis(PaneAxis { members, .. }) => {
@@ -5120,17 +5164,18 @@ impl Workspace {
}
fn serialize_workspace(&mut self, window: &mut Window, cx: &mut Context<Self>) {
- if self._schedule_serialize.is_none() {
- self._schedule_serialize = Some(cx.spawn_in(window, async move |this, cx| {
- cx.background_executor()
- .timer(Duration::from_millis(100))
- .await;
- this.update_in(cx, |this, window, cx| {
- this.serialize_workspace_internal(window, cx).detach();
- this._schedule_serialize.take();
- })
- .log_err();
- }));
+ if self._schedule_serialize_workspace.is_none() {
+ self._schedule_serialize_workspace =
+ Some(cx.spawn_in(window, async move |this, cx| {
+ cx.background_executor()
+ .timer(SERIALIZATION_THROTTLE_TIME)
+ .await;
+ this.update_in(cx, |this, window, cx| {
+ this.serialize_workspace_internal(window, cx).detach();
+ this._schedule_serialize_workspace.take();
+ })
+ .log_err();
+ }));
}
}