mirror of
https://github.com/zed-industries/zed.git
synced 2026-05-27 00:08:42 +00:00
### Summary Follow up on: https://github.com/zed-industries/zed/pull/53550 Threads in the sidebar are now filtered by remote connection in addition to path list. Previously, two different machines with the same absolute project paths (e.g. both at `/home/user/project`) would have their threads merged into a single sidebar group. Now each remote host gets its own group. To support this, this PR introduces `RemoteConnectionIdentity`, a normalized subset of `RemoteConnectionOptions` that includes only the fields relevant for identity matching (e.g. SSH host + username + port, but not nickname or connection timeout). All remote host comparisons in the sidebar, thread metadata store, and workspace persistence now go through `same_remote_connection_identity()` instead of raw `==` on `RemoteConnectionOptions`. #### Follow Up on remoting for sidebar 1. Make the neighbor in archive thread fallback to the main git worktree instead of the next thread 2. Start implementing remote git operations that archive feature depends on 3. Make remote connection pool equality use `RemoteConnectionIdentity` Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A --------- Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com> Co-authored-by: Richard Feldman <oss@rtfeldman.com>
168 lines
5.7 KiB
Rust
168 lines
5.7 KiB
Rust
use crate::RemoteConnectionOptions;
|
|
|
|
/// A normalized remote identity for matching live remote hosts against
|
|
/// persisted remote metadata.
|
|
///
|
|
/// This mirrors workspace persistence identity semantics rather than full
|
|
/// `RemoteConnectionOptions` equality, so runtime-only fields like SSH
|
|
/// nicknames or Docker environment overrides do not affect matching.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub enum RemoteConnectionIdentity {
|
|
Ssh {
|
|
host: String,
|
|
username: Option<String>,
|
|
port: Option<u16>,
|
|
},
|
|
Wsl {
|
|
distro_name: String,
|
|
user: Option<String>,
|
|
},
|
|
Docker {
|
|
container_id: String,
|
|
name: String,
|
|
remote_user: String,
|
|
},
|
|
#[cfg(any(test, feature = "test-support"))]
|
|
Mock { id: u64 },
|
|
}
|
|
|
|
impl From<&RemoteConnectionOptions> for RemoteConnectionIdentity {
|
|
fn from(options: &RemoteConnectionOptions) -> Self {
|
|
match options {
|
|
RemoteConnectionOptions::Ssh(options) => Self::Ssh {
|
|
host: options.host.to_string(),
|
|
username: options.username.clone(),
|
|
port: options.port,
|
|
},
|
|
RemoteConnectionOptions::Wsl(options) => Self::Wsl {
|
|
distro_name: options.distro_name.clone(),
|
|
user: options.user.clone(),
|
|
},
|
|
RemoteConnectionOptions::Docker(options) => Self::Docker {
|
|
container_id: options.container_id.clone(),
|
|
name: options.name.clone(),
|
|
remote_user: options.remote_user.clone(),
|
|
},
|
|
#[cfg(any(test, feature = "test-support"))]
|
|
RemoteConnectionOptions::Mock(options) => Self::Mock { id: options.id },
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn remote_connection_identity(options: &RemoteConnectionOptions) -> RemoteConnectionIdentity {
|
|
options.into()
|
|
}
|
|
|
|
pub fn same_remote_connection_identity(
|
|
left: Option<&RemoteConnectionOptions>,
|
|
right: Option<&RemoteConnectionOptions>,
|
|
) -> bool {
|
|
match (left, right) {
|
|
(Some(left), Some(right)) => {
|
|
remote_connection_identity(left) == remote_connection_identity(right)
|
|
}
|
|
(None, None) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::collections::BTreeMap;
|
|
|
|
use super::*;
|
|
use crate::{DockerConnectionOptions, SshConnectionOptions, WslConnectionOptions};
|
|
|
|
#[test]
|
|
fn ssh_identity_ignores_non_persisted_runtime_fields() {
|
|
let left = RemoteConnectionOptions::Ssh(SshConnectionOptions {
|
|
host: "example.com".into(),
|
|
username: Some("anth".to_string()),
|
|
port: Some(2222),
|
|
password: Some("secret".to_string()),
|
|
args: Some(vec!["-v".to_string()]),
|
|
connection_timeout: Some(30),
|
|
nickname: Some("work".to_string()),
|
|
upload_binary_over_ssh: true,
|
|
..Default::default()
|
|
});
|
|
let right = RemoteConnectionOptions::Ssh(SshConnectionOptions {
|
|
host: "example.com".into(),
|
|
username: Some("anth".to_string()),
|
|
port: Some(2222),
|
|
password: None,
|
|
args: None,
|
|
connection_timeout: None,
|
|
nickname: None,
|
|
upload_binary_over_ssh: false,
|
|
..Default::default()
|
|
});
|
|
|
|
assert!(same_remote_connection_identity(Some(&left), Some(&right),));
|
|
}
|
|
|
|
#[test]
|
|
fn ssh_identity_distinguishes_persistence_key_fields() {
|
|
let left = RemoteConnectionOptions::Ssh(SshConnectionOptions {
|
|
host: "example.com".into(),
|
|
username: Some("anth".to_string()),
|
|
port: Some(2222),
|
|
..Default::default()
|
|
});
|
|
let right = RemoteConnectionOptions::Ssh(SshConnectionOptions {
|
|
host: "example.com".into(),
|
|
username: Some("anth".to_string()),
|
|
port: Some(2223),
|
|
..Default::default()
|
|
});
|
|
|
|
assert!(!same_remote_connection_identity(Some(&left), Some(&right),));
|
|
}
|
|
|
|
#[test]
|
|
fn wsl_identity_includes_user() {
|
|
let left = RemoteConnectionOptions::Wsl(WslConnectionOptions {
|
|
distro_name: "Ubuntu".to_string(),
|
|
user: Some("anth".to_string()),
|
|
});
|
|
let right = RemoteConnectionOptions::Wsl(WslConnectionOptions {
|
|
distro_name: "Ubuntu".to_string(),
|
|
user: Some("root".to_string()),
|
|
});
|
|
|
|
assert!(!same_remote_connection_identity(Some(&left), Some(&right),));
|
|
}
|
|
|
|
#[test]
|
|
fn docker_identity_ignores_non_persisted_runtime_fields() {
|
|
let left = RemoteConnectionOptions::Docker(DockerConnectionOptions {
|
|
name: "zed-dev".to_string(),
|
|
container_id: "container-123".to_string(),
|
|
remote_user: "anth".to_string(),
|
|
upload_binary_over_docker_exec: true,
|
|
use_podman: true,
|
|
remote_env: BTreeMap::from([("FOO".to_string(), "BAR".to_string())]),
|
|
});
|
|
let right = RemoteConnectionOptions::Docker(DockerConnectionOptions {
|
|
name: "zed-dev".to_string(),
|
|
container_id: "container-123".to_string(),
|
|
remote_user: "anth".to_string(),
|
|
upload_binary_over_docker_exec: false,
|
|
use_podman: false,
|
|
remote_env: BTreeMap::new(),
|
|
});
|
|
|
|
assert!(same_remote_connection_identity(Some(&left), Some(&right),));
|
|
}
|
|
|
|
#[test]
|
|
fn local_identity_matches_only_local_identity() {
|
|
let remote = RemoteConnectionOptions::Wsl(WslConnectionOptions {
|
|
distro_name: "Ubuntu".to_string(),
|
|
user: Some("anth".to_string()),
|
|
});
|
|
|
|
assert!(same_remote_connection_identity(None, None));
|
|
assert!(!same_remote_connection_identity(None, Some(&remote)));
|
|
}
|
|
}
|