ssh: Expose server address in the title bar (#19549)
This PR exposes the server address (or the nickname, if there is one) on the title bar and in all modals that have the SSH header. The title bar tooltip meta description still shows the original server address (regardless of a nickname existing in this case), though. <img width="600" alt="Screenshot 2024-10-22 at 10 58 36" src="https://github.com/user-attachments/assets/64a94d9f-798b-44a4-9dee-6056886535bb"> Release Notes: - N/A --------- Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
parent
d8d8c908ed
commit
3ba2af289b
8 changed files with 119 additions and 15 deletions
|
@ -3,6 +3,7 @@ use std::path::PathBuf;
|
|||
use dev_server_projects::DevServer;
|
||||
use gpui::{ClickEvent, DismissEvent, EventEmitter, FocusHandle, FocusableView, Render, WeakView};
|
||||
use remote::SshConnectionOptions;
|
||||
use settings::Settings;
|
||||
use ui::{
|
||||
div, h_flex, rems, Button, ButtonCommon, ButtonStyle, Clickable, ElevationIndex, FluentBuilder,
|
||||
Headline, HeadlineSize, IconName, IconPosition, InteractiveElement, IntoElement, Label, Modal,
|
||||
|
@ -12,7 +13,7 @@ use workspace::{notifications::DetachAndPromptErr, ModalView, OpenOptions, Works
|
|||
|
||||
use crate::{
|
||||
open_dev_server_project, open_ssh_project, remote_servers::reconnect_to_dev_server_project,
|
||||
RemoteServerProjects,
|
||||
RemoteServerProjects, SshSettings,
|
||||
};
|
||||
|
||||
enum Host {
|
||||
|
@ -157,6 +158,16 @@ impl DisconnectedOverlay {
|
|||
let paths = ssh_project.paths.iter().map(PathBuf::from).collect();
|
||||
|
||||
cx.spawn(move |_, mut cx| async move {
|
||||
let nickname = cx
|
||||
.update(|cx| {
|
||||
SshSettings::get_global(cx).nickname_for(
|
||||
&connection_options.host,
|
||||
connection_options.port,
|
||||
&connection_options.username,
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
.flatten();
|
||||
open_ssh_project(
|
||||
connection_options,
|
||||
paths,
|
||||
|
@ -165,6 +176,7 @@ impl DisconnectedOverlay {
|
|||
replace_window: Some(window),
|
||||
..Default::default()
|
||||
},
|
||||
nickname,
|
||||
&mut cx,
|
||||
)
|
||||
.await?;
|
||||
|
|
|
@ -388,6 +388,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
};
|
||||
|
||||
let args = SshSettings::get_global(cx).args_for(&ssh_project.host, ssh_project.port, &ssh_project.user);
|
||||
let nickname = SshSettings::get_global(cx).nickname_for(&ssh_project.host, ssh_project.port, &ssh_project.user);
|
||||
let connection_options = SshConnectionOptions {
|
||||
host: ssh_project.host.clone(),
|
||||
username: ssh_project.user.clone(),
|
||||
|
@ -399,7 +400,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
let paths = ssh_project.paths.iter().map(PathBuf::from).collect();
|
||||
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
open_ssh_project(connection_options, paths, app_state, open_options, &mut cx).await
|
||||
open_ssh_project(connection_options, paths, app_state, open_options, nickname, &mut cx).await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ impl CreateRemoteServer {
|
|||
|
||||
struct ProjectPicker {
|
||||
connection_string: SharedString,
|
||||
nickname: Option<SharedString>,
|
||||
picker: View<Picker<OpenPathDelegate>>,
|
||||
_path_task: Shared<Task<Option<()>>>,
|
||||
}
|
||||
|
@ -191,7 +192,7 @@ impl FocusableView for ProjectPicker {
|
|||
impl ProjectPicker {
|
||||
fn new(
|
||||
ix: usize,
|
||||
connection_string: SharedString,
|
||||
connection: SshConnectionOptions,
|
||||
project: Model<Project>,
|
||||
workspace: WeakView<Workspace>,
|
||||
cx: &mut ViewContext<RemoteServerProjects>,
|
||||
|
@ -208,6 +209,12 @@ impl ProjectPicker {
|
|||
picker.set_query(query, cx);
|
||||
picker
|
||||
});
|
||||
let connection_string = connection.connection_string().into();
|
||||
let nickname = SshSettings::get_global(cx).nickname_for(
|
||||
&connection.host,
|
||||
connection.port,
|
||||
&connection.username,
|
||||
);
|
||||
cx.new_view(|cx| {
|
||||
let _path_task = cx
|
||||
.spawn({
|
||||
|
@ -293,6 +300,7 @@ impl ProjectPicker {
|
|||
_path_task,
|
||||
picker,
|
||||
connection_string,
|
||||
nickname,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -304,7 +312,7 @@ impl gpui::Render for ProjectPicker {
|
|||
.child(
|
||||
SshConnectionHeader {
|
||||
connection_string: self.connection_string.clone(),
|
||||
nickname: None,
|
||||
nickname: self.nickname.clone(),
|
||||
}
|
||||
.render(cx),
|
||||
)
|
||||
|
@ -380,7 +388,7 @@ impl RemoteServerProjects {
|
|||
let mut this = Self::new(cx, workspace.clone());
|
||||
this.mode = Mode::ProjectPicker(ProjectPicker::new(
|
||||
ix,
|
||||
connection_options.connection_string().into(),
|
||||
connection_options,
|
||||
project,
|
||||
workspace,
|
||||
cx,
|
||||
|
@ -408,7 +416,7 @@ impl RemoteServerProjects {
|
|||
return;
|
||||
}
|
||||
};
|
||||
let ssh_prompt = cx.new_view(|cx| SshPrompt::new(&connection_options, cx));
|
||||
let ssh_prompt = cx.new_view(|cx| SshPrompt::new(&connection_options, None, cx));
|
||||
|
||||
let connection = connect_over_ssh(
|
||||
connection_options.remote_server_identifier(),
|
||||
|
@ -485,10 +493,13 @@ impl RemoteServerProjects {
|
|||
return;
|
||||
};
|
||||
|
||||
let nickname = ssh_connection.nickname.clone();
|
||||
let connection_options = ssh_connection.into();
|
||||
workspace.update(cx, |_, cx| {
|
||||
cx.defer(move |workspace, cx| {
|
||||
workspace.toggle_modal(cx, |cx| SshConnectionModal::new(&connection_options, cx));
|
||||
workspace.toggle_modal(cx, |cx| {
|
||||
SshConnectionModal::new(&connection_options, nickname, cx)
|
||||
});
|
||||
let prompt = workspace
|
||||
.active_modal::<SshConnectionModal>(cx)
|
||||
.unwrap()
|
||||
|
@ -737,11 +748,13 @@ impl RemoteServerProjects {
|
|||
let project = project.clone();
|
||||
let server = server.clone();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let nickname = server.nickname.clone();
|
||||
let result = open_ssh_project(
|
||||
server.into(),
|
||||
project.paths.into_iter().map(PathBuf::from).collect(),
|
||||
app_state,
|
||||
OpenOptions::default(),
|
||||
nickname,
|
||||
&mut cx,
|
||||
)
|
||||
.await;
|
||||
|
|
|
@ -52,6 +52,23 @@ impl SshSettings {
|
|||
})
|
||||
.next()
|
||||
}
|
||||
pub fn nickname_for(
|
||||
&self,
|
||||
host: &str,
|
||||
port: Option<u16>,
|
||||
user: &Option<String>,
|
||||
) -> Option<SharedString> {
|
||||
self.ssh_connections()
|
||||
.filter_map(|conn| {
|
||||
if conn.host == host && &conn.username == user && conn.port == port {
|
||||
Some(conn.nickname)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
|
@ -103,6 +120,7 @@ impl Settings for SshSettings {
|
|||
|
||||
pub struct SshPrompt {
|
||||
connection_string: SharedString,
|
||||
nickname: Option<SharedString>,
|
||||
status_message: Option<SharedString>,
|
||||
prompt: Option<(View<Markdown>, oneshot::Sender<Result<String>>)>,
|
||||
editor: View<Editor>,
|
||||
|
@ -116,11 +134,13 @@ pub struct SshConnectionModal {
|
|||
impl SshPrompt {
|
||||
pub(crate) fn new(
|
||||
connection_options: &SshConnectionOptions,
|
||||
nickname: Option<SharedString>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let connection_string = connection_options.connection_string().into();
|
||||
Self {
|
||||
connection_string,
|
||||
nickname,
|
||||
status_message: None,
|
||||
prompt: None,
|
||||
editor: cx.new_view(Editor::single_line),
|
||||
|
@ -228,9 +248,13 @@ impl Render for SshPrompt {
|
|||
}
|
||||
|
||||
impl SshConnectionModal {
|
||||
pub fn new(connection_options: &SshConnectionOptions, cx: &mut ViewContext<Self>) -> Self {
|
||||
pub(crate) fn new(
|
||||
connection_options: &SshConnectionOptions,
|
||||
nickname: Option<SharedString>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
Self {
|
||||
prompt: cx.new_view(|cx| SshPrompt::new(connection_options, cx)),
|
||||
prompt: cx.new_view(|cx| SshPrompt::new(connection_options, nickname, cx)),
|
||||
finished: false,
|
||||
}
|
||||
}
|
||||
|
@ -297,6 +321,7 @@ impl RenderOnce for SshConnectionHeader {
|
|||
|
||||
impl Render for SshConnectionModal {
|
||||
fn render(&mut self, cx: &mut ui::ViewContext<Self>) -> impl ui::IntoElement {
|
||||
let nickname = self.prompt.read(cx).nickname.clone();
|
||||
let connection_string = self.prompt.read(cx).connection_string.clone();
|
||||
let theme = cx.theme();
|
||||
|
||||
|
@ -313,7 +338,7 @@ impl Render for SshConnectionModal {
|
|||
.child(
|
||||
SshConnectionHeader {
|
||||
connection_string,
|
||||
nickname: None,
|
||||
nickname,
|
||||
}
|
||||
.render(cx),
|
||||
)
|
||||
|
@ -589,6 +614,7 @@ pub async fn open_ssh_project(
|
|||
paths: Vec<PathBuf>,
|
||||
app_state: Arc<AppState>,
|
||||
open_options: workspace::OpenOptions,
|
||||
nickname: Option<SharedString>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
let window = if let Some(window) = open_options.replace_window {
|
||||
|
@ -612,9 +638,12 @@ pub async fn open_ssh_project(
|
|||
loop {
|
||||
let delegate = window.update(cx, {
|
||||
let connection_options = connection_options.clone();
|
||||
let nickname = nickname.clone();
|
||||
move |workspace, cx| {
|
||||
cx.activate_window();
|
||||
workspace.toggle_modal(cx, |cx| SshConnectionModal::new(&connection_options, cx));
|
||||
workspace.toggle_modal(cx, |cx| {
|
||||
SshConnectionModal::new(&connection_options, nickname.clone(), cx)
|
||||
});
|
||||
let ui = workspace
|
||||
.active_modal::<SshConnectionModal>(cx)
|
||||
.unwrap()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue