recent_projects: Move SSH server entry to initialize once instead of every render (#31650)
Currently, `RemoteEntry::SshConfig` for `ssh_config_servers` initializes on every render. This leads to side effects like a new focus handle being created on every render, which leads to breaking navigating up/down for `ssh_config_servers` items. This PR fixes it by moving the logic of remote entry for`ssh_config_servers` into `default_mode`, and only rebuilding it when `ssh_config_servers` actually changes. Before: https://github.com/user-attachments/assets/8c7187d3-16b5-4f96-aa73-fe4f8227b7d0 After: https://github.com/user-attachments/assets/21588628-8b1c-43fb-bcb8-0b93c70a1e2b Release Notes: - Fixed issue navigating SSH config servers in Remote Projects with keyboard.
This commit is contained in:
parent
5173a1a968
commit
ea8a3be91b
1 changed files with 62 additions and 38 deletions
|
@ -289,15 +289,18 @@ struct DefaultState {
|
||||||
scrollbar: ScrollbarState,
|
scrollbar: ScrollbarState,
|
||||||
add_new_server: NavigableEntry,
|
add_new_server: NavigableEntry,
|
||||||
servers: Vec<RemoteEntry>,
|
servers: Vec<RemoteEntry>,
|
||||||
handle: ScrollHandle,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultState {
|
impl DefaultState {
|
||||||
fn new(cx: &mut App) -> Self {
|
fn new(ssh_config_servers: &BTreeSet<SharedString>, cx: &mut App) -> Self {
|
||||||
let handle = ScrollHandle::new();
|
let handle = ScrollHandle::new();
|
||||||
let scrollbar = ScrollbarState::new(handle.clone());
|
let scrollbar = ScrollbarState::new(handle.clone());
|
||||||
let add_new_server = NavigableEntry::new(&handle, cx);
|
let add_new_server = NavigableEntry::new(&handle, cx);
|
||||||
let servers = SshSettings::get_global(cx)
|
|
||||||
|
let ssh_settings = SshSettings::get_global(cx);
|
||||||
|
let read_ssh_config = ssh_settings.read_ssh_config;
|
||||||
|
|
||||||
|
let mut servers: Vec<RemoteEntry> = ssh_settings
|
||||||
.ssh_connections()
|
.ssh_connections()
|
||||||
.map(|connection| {
|
.map(|connection| {
|
||||||
let open_folder = NavigableEntry::new(&handle, cx);
|
let open_folder = NavigableEntry::new(&handle, cx);
|
||||||
|
@ -316,11 +319,25 @@ impl DefaultState {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
if read_ssh_config {
|
||||||
|
let mut extra_servers_from_config = ssh_config_servers.clone();
|
||||||
|
for server in &servers {
|
||||||
|
if let RemoteEntry::Project { connection, .. } = server {
|
||||||
|
extra_servers_from_config.remove(&connection.host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
servers.extend(extra_servers_from_config.into_iter().map(|host| {
|
||||||
|
RemoteEntry::SshConfig {
|
||||||
|
open_folder: NavigableEntry::new(&handle, cx),
|
||||||
|
host,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
scrollbar,
|
scrollbar,
|
||||||
add_new_server,
|
add_new_server,
|
||||||
servers,
|
servers,
|
||||||
handle,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,8 +357,8 @@ enum Mode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mode {
|
impl Mode {
|
||||||
fn default_mode(cx: &mut App) -> Self {
|
fn default_mode(ssh_config_servers: &BTreeSet<SharedString>, cx: &mut App) -> Self {
|
||||||
Self::Default(DefaultState::new(cx))
|
Self::Default(DefaultState::new(ssh_config_servers, cx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl RemoteServerProjects {
|
impl RemoteServerProjects {
|
||||||
|
@ -404,7 +421,7 @@ impl RemoteServerProjects {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
mode: Mode::default_mode(cx),
|
mode: Mode::default_mode(&BTreeSet::new(), cx),
|
||||||
focus_handle,
|
focus_handle,
|
||||||
workspace,
|
workspace,
|
||||||
retained_connections: Vec::new(),
|
retained_connections: Vec::new(),
|
||||||
|
@ -481,7 +498,7 @@ impl RemoteServerProjects {
|
||||||
telemetry::event!("SSH Server Created");
|
telemetry::event!("SSH Server Created");
|
||||||
this.retained_connections.push(client);
|
this.retained_connections.push(client);
|
||||||
this.add_ssh_server(connection_options, cx);
|
this.add_ssh_server(connection_options, cx);
|
||||||
this.mode = Mode::default_mode(cx);
|
this.mode = Mode::default_mode(&this.ssh_config_servers, cx);
|
||||||
this.focus_handle(cx).focus(window);
|
this.focus_handle(cx).focus(window);
|
||||||
cx.notify()
|
cx.notify()
|
||||||
})
|
})
|
||||||
|
@ -648,7 +665,7 @@ impl RemoteServerProjects {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.mode = Mode::default_mode(cx);
|
self.mode = Mode::default_mode(&self.ssh_config_servers, cx);
|
||||||
self.focus_handle.focus(window);
|
self.focus_handle.focus(window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -668,7 +685,7 @@ impl RemoteServerProjects {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.mode = Mode::default_mode(cx);
|
self.mode = Mode::default_mode(&self.ssh_config_servers, cx);
|
||||||
self.focus_handle(cx).focus(window);
|
self.focus_handle(cx).focus(window);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
@ -1229,7 +1246,10 @@ impl RemoteServerProjects {
|
||||||
.ok();
|
.ok();
|
||||||
remote_servers
|
remote_servers
|
||||||
.update(cx, |this, cx| {
|
.update(cx, |this, cx| {
|
||||||
this.mode = Mode::default_mode(cx);
|
this.mode = Mode::default_mode(
|
||||||
|
&this.ssh_config_servers,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -1281,7 +1301,7 @@ impl RemoteServerProjects {
|
||||||
.id("ssh-options-copy-server-address")
|
.id("ssh-options-copy-server-address")
|
||||||
.track_focus(&entries[3].focus_handle)
|
.track_focus(&entries[3].focus_handle)
|
||||||
.on_action(cx.listener(|this, _: &menu::Confirm, window, cx| {
|
.on_action(cx.listener(|this, _: &menu::Confirm, window, cx| {
|
||||||
this.mode = Mode::default_mode(cx);
|
this.mode = Mode::default_mode(&this.ssh_config_servers, cx);
|
||||||
cx.focus_self(window);
|
cx.focus_self(window);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}))
|
}))
|
||||||
|
@ -1297,7 +1317,8 @@ impl RemoteServerProjects {
|
||||||
)
|
)
|
||||||
.child(Label::new("Go Back"))
|
.child(Label::new("Go Back"))
|
||||||
.on_click(cx.listener(|this, _, window, cx| {
|
.on_click(cx.listener(|this, _, window, cx| {
|
||||||
this.mode = Mode::default_mode(cx);
|
this.mode =
|
||||||
|
Mode::default_mode(&this.ssh_config_servers, cx);
|
||||||
cx.focus_self(window);
|
cx.focus_self(window);
|
||||||
cx.notify()
|
cx.notify()
|
||||||
})),
|
})),
|
||||||
|
@ -1358,7 +1379,8 @@ impl RemoteServerProjects {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let ssh_settings = SshSettings::get_global(cx);
|
let ssh_settings = SshSettings::get_global(cx);
|
||||||
let read_ssh_config = ssh_settings.read_ssh_config;
|
let mut should_rebuild = false;
|
||||||
|
|
||||||
if ssh_settings
|
if ssh_settings
|
||||||
.ssh_connections
|
.ssh_connections
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -1373,32 +1395,34 @@ impl RemoteServerProjects {
|
||||||
.ne(connections.iter())
|
.ne(connections.iter())
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
self.mode = Mode::default_mode(cx);
|
should_rebuild = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if !should_rebuild && ssh_settings.read_ssh_config {
|
||||||
|
let current_ssh_hosts: BTreeSet<SharedString> = state
|
||||||
|
.servers
|
||||||
|
.iter()
|
||||||
|
.filter_map(|server| match server {
|
||||||
|
RemoteEntry::SshConfig { host, .. } => Some(host.clone()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mut expected_ssh_hosts = self.ssh_config_servers.clone();
|
||||||
|
for server in &state.servers {
|
||||||
|
if let RemoteEntry::Project { connection, .. } = server {
|
||||||
|
expected_ssh_hosts.remove(&connection.host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
should_rebuild = current_ssh_hosts != expected_ssh_hosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if should_rebuild {
|
||||||
|
self.mode = Mode::default_mode(&self.ssh_config_servers, cx);
|
||||||
if let Mode::Default(new_state) = &self.mode {
|
if let Mode::Default(new_state) = &self.mode {
|
||||||
state = new_state.clone();
|
state = new_state.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut extra_servers_from_config = if read_ssh_config {
|
|
||||||
self.ssh_config_servers.clone()
|
|
||||||
} else {
|
|
||||||
BTreeSet::new()
|
|
||||||
};
|
|
||||||
let mut servers = state.servers.clone();
|
|
||||||
for server in &servers {
|
|
||||||
if let RemoteEntry::Project { connection, .. } = server {
|
|
||||||
extra_servers_from_config.remove(&connection.host);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
servers.extend(
|
|
||||||
extra_servers_from_config
|
|
||||||
.into_iter()
|
|
||||||
.map(|host| RemoteEntry::SshConfig {
|
|
||||||
open_folder: NavigableEntry::new(&state.handle, cx),
|
|
||||||
host,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let scroll_state = state.scrollbar.parent_entity(&cx.entity());
|
let scroll_state = state.scrollbar.parent_entity(&cx.entity());
|
||||||
let connect_button = div()
|
let connect_button = div()
|
||||||
.id("ssh-connect-new-server-container")
|
.id("ssh-connect-new-server-container")
|
||||||
|
@ -1455,7 +1479,7 @@ impl RemoteServerProjects {
|
||||||
)
|
)
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
)
|
)
|
||||||
.children(servers.iter().enumerate().map(|(ix, connection)| {
|
.children(state.servers.iter().enumerate().map(|(ix, connection)| {
|
||||||
self.render_ssh_connection(ix, connection.clone(), window, cx)
|
self.render_ssh_connection(ix, connection.clone(), window, cx)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
})),
|
})),
|
||||||
|
@ -1464,7 +1488,7 @@ impl RemoteServerProjects {
|
||||||
)
|
)
|
||||||
.entry(state.add_new_server.clone());
|
.entry(state.add_new_server.clone());
|
||||||
|
|
||||||
for server in &servers {
|
for server in &state.servers {
|
||||||
match server {
|
match server {
|
||||||
RemoteEntry::Project {
|
RemoteEntry::Project {
|
||||||
open_folder,
|
open_folder,
|
||||||
|
@ -1556,7 +1580,7 @@ impl RemoteServerProjects {
|
||||||
},
|
},
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
self.mode = Mode::default_mode(cx);
|
self.mode = Mode::default_mode(&self.ssh_config_servers, cx);
|
||||||
new_ix.load(atomic::Ordering::Acquire)
|
new_ix.load(atomic::Ordering::Acquire)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue